Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(153)

Side by Side Diff: chrome/browser/media/media_stream_capture_indicator.cc

Issue 12035046: Fix bug causing tab favicon media indicator to not turn off. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/media/media_stream_capture_indicator.h" 5 #include "chrome/browser/media/media_stream_capture_indicator.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/i18n/rtl.h" 8 #include "base/i18n/rtl.h"
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
10 #include "base/utf_string_conversions.h" 11 #include "base/utf_string_conversions.h"
11 #include "chrome/app/chrome_command_ids.h" 12 #include "chrome/app/chrome_command_ids.h"
12 #include "chrome/browser/browser_process.h" 13 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/extensions/extension_service.h" 14 #include "chrome/browser/extensions/extension_service.h"
14 #include "chrome/browser/extensions/image_loader.h" 15 #include "chrome/browser/extensions/image_loader.h"
15 #include "chrome/browser/profiles/profile.h" 16 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/status_icons/status_icon.h" 17 #include "chrome/browser/status_icons/status_icon.h"
17 #include "chrome/browser/status_icons/status_tray.h" 18 #include "chrome/browser/status_icons/status_tray.h"
18 #include "chrome/browser/tab_contents/tab_util.h" 19 #include "chrome/browser/tab_contents/tab_util.h"
19 #include "content/public/browser/browser_thread.h" 20 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/content_browser_client.h" 21 #include "content/public/browser/content_browser_client.h"
21 #include "content/public/browser/invalidate_type.h" 22 #include "content/public/browser/invalidate_type.h"
22 #include "content/public/browser/render_view_host.h"
23 #include "content/public/browser/web_contents.h" 23 #include "content/public/browser/web_contents.h"
24 #include "content/public/browser/web_contents_delegate.h" 24 #include "content/public/browser/web_contents_delegate.h"
25 #include "content/public/common/media_stream_request.h"
26 #include "grit/chromium_strings.h" 25 #include "grit/chromium_strings.h"
27 #include "grit/generated_resources.h" 26 #include "grit/generated_resources.h"
28 #include "grit/theme_resources.h" 27 #include "grit/theme_resources.h"
29 #include "net/base/net_util.h" 28 #include "net/base/net_util.h"
30 #include "ui/base/l10n/l10n_util.h" 29 #include "ui/base/l10n/l10n_util.h"
31 #include "ui/base/resource/resource_bundle.h" 30 #include "ui/base/resource/resource_bundle.h"
31 #include "ui/gfx/image/image_skia.h"
32 32
33 using content::BrowserThread; 33 using content::BrowserThread;
34 using content::WebContents; 34 using content::WebContents;
35 35
36 namespace { 36 namespace {
37 37
38 const extensions::Extension* GetExtension(int render_process_id, 38 const extensions::Extension* GetExtension(WebContents* web_contents) {
39 int render_view_id) {
40 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 39 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
41 40
42 WebContents* web_contents = tab_util::GetWebContentsByID(
43 render_process_id, render_view_id);
44 if (!web_contents) 41 if (!web_contents)
45 return NULL; 42 return NULL;
46 43
47 Profile* profile = 44 Profile* profile =
48 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 45 Profile::FromBrowserContext(web_contents->GetBrowserContext());
49 if (!profile) 46 if (!profile)
50 return NULL; 47 return NULL;
51 48
52 ExtensionService* extension_service = profile->GetExtensionService(); 49 ExtensionService* extension_service = profile->GetExtensionService();
53 if (!extension_service) 50 if (!extension_service)
54 return NULL; 51 return NULL;
55 52
56 return extension_service->extensions()->GetExtensionOrAppByURL( 53 return extension_service->extensions()->GetExtensionOrAppByURL(
57 ExtensionURLInfo(web_contents->GetURL())); 54 ExtensionURLInfo(web_contents->GetURL()));
58 } 55 }
59 56
60 // Gets the security originator of the tab. It returns a string with no '/' 57 // Gets the security originator of the tab. It returns a string with no '/'
61 // at the end to display in the UI. 58 // at the end to display in the UI.
62 string16 GetSecurityOrigin(int render_process_id, int render_view_id) { 59 string16 GetSecurityOrigin(WebContents* web_contents) {
63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 60 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
64 WebContents* tab_content = tab_util::GetWebContentsByID( 61
65 render_process_id, render_view_id); 62 if (!web_contents)
66 if (!tab_content)
67 return string16(); 63 return string16();
68 64
69 std::string security_origin = tab_content->GetURL().GetOrigin().spec(); 65 std::string security_origin = web_contents->GetURL().GetOrigin().spec();
70 66
71 // Remove the last character if it is a '/'. 67 // Remove the last character if it is a '/'.
72 if (!security_origin.empty()) { 68 if (!security_origin.empty()) {
73 std::string::iterator it = security_origin.end() - 1; 69 std::string::iterator it = security_origin.end() - 1;
74 if (*it == '/') 70 if (*it == '/')
75 security_origin.erase(it); 71 security_origin.erase(it);
76 } 72 }
77 73
78 return UTF8ToUTF16(security_origin); 74 return UTF8ToUTF16(security_origin);
79 } 75 }
80 76
81 string16 GetTitle(int render_process_id, int render_view_id) { 77 string16 GetTitle(WebContents* web_contents) {
82 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 78 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
83 79
84 const extensions::Extension* extension = 80 if (!web_contents)
85 GetExtension(render_process_id, render_view_id); 81 return string16();
82
83 const extensions::Extension* const extension = GetExtension(web_contents);
86 if (extension) 84 if (extension)
87 return UTF8ToUTF16(extension->name()); 85 return UTF8ToUTF16(extension->name());
88 86
89 WebContents* tab_content = tab_util::GetWebContentsByID( 87 string16 tab_title = web_contents->GetTitle();
90 render_process_id, render_view_id);
91 if (!tab_content)
92 return string16();
93
94 string16 tab_title = tab_content->GetTitle();
95 88
96 if (tab_title.empty()) { 89 if (tab_title.empty()) {
97 // If the page's title is empty use its security originator. 90 // If the page's title is empty use its security originator.
98 tab_title = GetSecurityOrigin(render_process_id, render_view_id); 91 tab_title = GetSecurityOrigin(web_contents);
99 } else { 92 } else {
100 // If the page's title matches its URL, use its security originator. 93 // If the page's title matches its URL, use its security originator.
101 std::string languages = 94 std::string languages =
102 content::GetContentClient()->browser()->GetAcceptLangs( 95 content::GetContentClient()->browser()->GetAcceptLangs(
103 tab_content->GetBrowserContext()); 96 web_contents->GetBrowserContext());
104 if (tab_title == net::FormatUrl(tab_content->GetURL(), languages)) 97 if (tab_title == net::FormatUrl(web_contents->GetURL(), languages))
105 tab_title = GetSecurityOrigin(render_process_id, render_view_id); 98 tab_title = GetSecurityOrigin(web_contents);
106 } 99 }
107 100
108 return tab_title; 101 return tab_title;
109 } 102 }
110 103
104 void CountDevices(const content::MediaStreamDevices& devices,
105 int* num_audio, int* num_video, int* num_mirroring) {
106 *num_audio = 0;
107 *num_video = 0;
108 *num_mirroring = 0;
109 for (content::MediaStreamDevices::const_iterator it = devices.begin();
110 it != devices.end(); ++it) {
111 if (it->type == content::MEDIA_TAB_AUDIO_CAPTURE ||
112 it->type == content::MEDIA_TAB_VIDEO_CAPTURE) {
113 ++(*num_mirroring);
114 } else if (content::IsAudioMediaType(it->type)) {
115 ++(*num_audio);
116 } else if (content::IsVideoMediaType(it->type)) {
117 ++(*num_video);
118 } else {
119 NOTIMPLEMENTED();
120 }
121 }
122 }
123
111 } // namespace 124 } // namespace
112 125
113 MediaStreamCaptureIndicator::TabEquals::TabEquals(WebContents* web_contents, 126 // Stores usage counts for all the capture devices associated with a single
114 int render_process_id, 127 // WebContents instance.
115 int render_view_id) 128 class MediaStreamCaptureIndicator::CaptureDeviceUsage {
116 : web_contents_(web_contents), 129 public:
117 render_process_id_(render_process_id), 130 CaptureDeviceUsage();
118 render_view_id_(render_view_id) {}
119 131
120 bool MediaStreamCaptureIndicator::TabEquals::operator() ( 132 bool IsCapturingAudio() const { return audio_ref_count_ > 0; }
121 const MediaStreamCaptureIndicator::CaptureDeviceTab& tab) { 133 bool IsCapturingVideo() const { return video_ref_count_ > 0; }
122 return (web_contents_ == tab.web_contents || 134 bool IsMirroring() const { return mirroring_ref_count_ > 0; }
123 (render_process_id_ == tab.render_process_id && 135
124 render_view_id_ == tab.render_view_id)); 136 void TallyUsage(int num_audio, int num_video, int num_mirroring);
137
138 private:
139 int audio_ref_count_;
140 int video_ref_count_;
141 int mirroring_ref_count_;
142 };
143
144 MediaStreamCaptureIndicator::CaptureDeviceUsage::CaptureDeviceUsage()
145 : audio_ref_count_(0), video_ref_count_(0), mirroring_ref_count_(0) {
146 }
147
148 void MediaStreamCaptureIndicator::CaptureDeviceUsage::TallyUsage(
149 int num_audio, int num_video, int num_mirroring) {
150 audio_ref_count_ += num_audio;
151 DCHECK_LE(0, audio_ref_count_);
152 video_ref_count_ += num_video;
153 DCHECK_LE(0, video_ref_count_);
154 mirroring_ref_count_ += num_mirroring;
155 DCHECK_LE(0, mirroring_ref_count_);
125 } 156 }
126 157
127 MediaStreamCaptureIndicator::MediaStreamCaptureIndicator() 158 MediaStreamCaptureIndicator::MediaStreamCaptureIndicator()
128 : status_icon_(NULL), 159 : status_icon_(NULL),
129 mic_image_(NULL), 160 mic_image_(NULL),
130 camera_image_(NULL), 161 camera_image_(NULL),
131 balloon_image_(NULL), 162 balloon_image_(NULL),
132 should_show_balloon_(false) { 163 should_show_balloon_(false) {
133 } 164 }
134 165
135 MediaStreamCaptureIndicator::~MediaStreamCaptureIndicator() { 166 MediaStreamCaptureIndicator::~MediaStreamCaptureIndicator() {
136 // The user is responsible for cleaning up by closing all the opened devices. 167 // The user is responsible for cleaning up by reporting the closure of any
137 DCHECK(tabs_.empty()); 168 // opened devices.
169 DCHECK(usage_map_.empty());
138 } 170 }
139 171
140 bool MediaStreamCaptureIndicator::IsCommandIdChecked( 172 bool MediaStreamCaptureIndicator::IsCommandIdChecked(
141 int command_id) const { 173 int command_id) const {
142 NOTIMPLEMENTED() << "There are no checked items in the MediaStream menu."; 174 NOTIMPLEMENTED() << "There are no checked items in the MediaStream menu.";
143 return false; 175 return false;
144 } 176 }
145 177
146 bool MediaStreamCaptureIndicator::IsCommandIdEnabled( 178 bool MediaStreamCaptureIndicator::IsCommandIdEnabled(
147 int command_id) const { 179 int command_id) const {
148 return command_id != IDC_MinimumLabelValue; 180 return command_id != IDC_MinimumLabelValue;
149 } 181 }
150 182
151 bool MediaStreamCaptureIndicator::GetAcceleratorForCommandId( 183 bool MediaStreamCaptureIndicator::GetAcceleratorForCommandId(
152 int command_id, ui::Accelerator* accelerator) { 184 int command_id, ui::Accelerator* accelerator) {
153 // No accelerators for status icon context menu. 185 // No accelerators for status icon context menu.
154 return false; 186 return false;
155 } 187 }
156 188
157 void MediaStreamCaptureIndicator::ExecuteCommand(int command_id) { 189 void MediaStreamCaptureIndicator::ExecuteCommand(int command_id) {
158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
159 DCHECK(command_id >= IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_FIRST && 191
justinlin 2013/01/23 19:30:43 nit: This previous DCHECK seems more clear to me?
miu 2013/01/23 20:33:28 They're different. The old DCHECK just checks whe
160 command_id <= IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_LAST); 192 const int index =
161 int index = command_id - IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_FIRST; 193 command_id - IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_FIRST;
162 WebContents* web_content = tab_util::GetWebContentsByID( 194 DCHECK_LE(0, index);
163 tabs_[index].render_process_id, tabs_[index].render_view_id); 195 DCHECK_GT(static_cast<int>(command_map_.size()), index);
164 DCHECK(web_content); 196 WebContents* const web_contents = command_map_[index];
165 if (!web_content) { 197 if (!web_contents) {
166 NOTREACHED(); 198 NOTREACHED();
167 return; 199 return;
168 } 200 }
169 201
170 web_content->GetDelegate()->ActivateContents(web_content); 202 web_contents->GetDelegate()->ActivateContents(web_contents);
171 } 203 }
172 204
173 void MediaStreamCaptureIndicator::CaptureDevicesOpened( 205 void MediaStreamCaptureIndicator::CaptureDevicesOpened(
174 int render_process_id, 206 int render_process_id,
175 int render_view_id, 207 int render_view_id,
176 const content::MediaStreamDevices& devices) { 208 const content::MediaStreamDevices& devices) {
177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 209 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
178 DCHECK(!devices.empty()); 210 DCHECK(!devices.empty());
179 211
180 BrowserThread::PostTask( 212 BrowserThread::PostTask(
(...skipping 14 matching lines...) Expand all
195 base::Bind(&MediaStreamCaptureIndicator::DoDevicesClosedOnUIThread, 227 base::Bind(&MediaStreamCaptureIndicator::DoDevicesClosedOnUIThread,
196 this, render_process_id, render_view_id, devices)); 228 this, render_process_id, render_view_id, devices));
197 } 229 }
198 230
199 void MediaStreamCaptureIndicator::DoDevicesOpenedOnUIThread( 231 void MediaStreamCaptureIndicator::DoDevicesOpenedOnUIThread(
200 int render_process_id, 232 int render_process_id,
201 int render_view_id, 233 int render_view_id,
202 const content::MediaStreamDevices& devices) { 234 const content::MediaStreamDevices& devices) {
203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 235 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
204 236
205 CreateStatusTray(); 237 AddCaptureDevices(render_process_id, render_view_id, devices);
justinlin 2013/01/23 19:30:43 Did you mean to remove this?
miu 2013/01/23 20:33:28 Yes. Two things happening here: 1. I renamed the
justinlin 2013/01/24 07:04:44 OK. I'm probably nitpicking too much, but could we
miu 2013/01/28 19:36:51 I'm not sure. I'll have to try this out to see wh
justinlin 2013/01/28 22:31:21 I meant in that case, we don't have a web_contents
206
207 AddCaptureDeviceTab(render_process_id, render_view_id, devices);
208 } 238 }
209 239
210 void MediaStreamCaptureIndicator::DoDevicesClosedOnUIThread( 240 void MediaStreamCaptureIndicator::DoDevicesClosedOnUIThread(
211 int render_process_id, 241 int render_process_id,
212 int render_view_id, 242 int render_view_id,
213 const content::MediaStreamDevices& devices) { 243 const content::MediaStreamDevices& devices) {
214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 244 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
215 245
216 RemoveCaptureDeviceTab(render_process_id, render_view_id, devices); 246 RemoveCaptureDevices(render_process_id, render_view_id, devices);
217 } 247 }
218 248
219 void MediaStreamCaptureIndicator::CreateStatusTray() { 249 void MediaStreamCaptureIndicator::MaybeCreateStatusTrayIcon() {
220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
221 if (status_icon_) 251 if (status_icon_)
222 return; 252 return;
223 253
224 // If there is no browser process, we should not create the status tray. 254 // If there is no browser process, we should not create the status tray.
225 if (!g_browser_process) 255 if (!g_browser_process)
226 return; 256 return;
227 257
228 StatusTray* status_tray = g_browser_process->status_tray(); 258 StatusTray* status_tray = g_browser_process->status_tray();
229 if (!status_tray) 259 if (!status_tray)
(...skipping 17 matching lines...) Expand all
247 if (!balloon_image_) { 277 if (!balloon_image_) {
248 balloon_image_ = ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 278 balloon_image_ = ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
249 IDR_PRODUCT_LOGO_32); 279 IDR_PRODUCT_LOGO_32);
250 } 280 }
251 DCHECK(mic_image_); 281 DCHECK(mic_image_);
252 DCHECK(camera_image_); 282 DCHECK(camera_image_);
253 DCHECK(balloon_image_); 283 DCHECK(balloon_image_);
254 } 284 }
255 285
256 void MediaStreamCaptureIndicator::ShowBalloon( 286 void MediaStreamCaptureIndicator::ShowBalloon(
257 int render_process_id, 287 WebContents* web_contents, bool audio, bool video) {
258 int render_view_id,
259 bool audio,
260 bool video) {
261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 288 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
262 DCHECK(audio || video); 289 DCHECK(audio || video);
263 290
264 int message_id = IDS_MEDIA_STREAM_STATUS_TRAY_BALLOON_BODY_AUDIO_AND_VIDEO; 291 int message_id = IDS_MEDIA_STREAM_STATUS_TRAY_BALLOON_BODY_AUDIO_AND_VIDEO;
265 if (audio && !video) 292 if (audio && !video)
266 message_id = IDS_MEDIA_STREAM_STATUS_TRAY_BALLOON_BODY_AUDIO_ONLY; 293 message_id = IDS_MEDIA_STREAM_STATUS_TRAY_BALLOON_BODY_AUDIO_ONLY;
267 else if (!audio && video) 294 else if (!audio && video)
268 message_id = IDS_MEDIA_STREAM_STATUS_TRAY_BALLOON_BODY_VIDEO_ONLY; 295 message_id = IDS_MEDIA_STREAM_STATUS_TRAY_BALLOON_BODY_VIDEO_ONLY;
269 296
270 // Only show the balloon for extensions. 297 // Only show the balloon for extensions.
271 const extensions::Extension* extension = 298 const extensions::Extension* const extension = GetExtension(web_contents);
272 GetExtension(render_process_id, render_view_id);
273 if (!extension) { 299 if (!extension) {
274 DVLOG(1) << "Balloon is shown only for extensions"; 300 DVLOG(1) << "Balloon is shown only for extensions";
275 return; 301 return;
276 } 302 }
277 303
278 string16 message = 304 string16 message =
279 l10n_util::GetStringFUTF16(message_id, 305 l10n_util::GetStringFUTF16(message_id,
280 UTF8ToUTF16(extension->name())); 306 UTF8ToUTF16(extension->name()));
281
282 WebContents* web_contents = tab_util::GetWebContentsByID(
283 render_process_id, render_view_id);
284
285 Profile* profile = 307 Profile* profile =
286 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 308 Profile::FromBrowserContext(web_contents->GetBrowserContext());
287 309
288 should_show_balloon_ = true; 310 should_show_balloon_ = true;
289 extensions::ImageLoader::Get(profile)->LoadImageAsync( 311 extensions::ImageLoader::Get(profile)->LoadImageAsync(
290 extension, 312 extension,
291 extension->GetIconResource(32, ExtensionIconSet::MATCH_BIGGER), 313 extension->GetIconResource(32, ExtensionIconSet::MATCH_BIGGER),
292 gfx::Size(32, 32), 314 gfx::Size(32, 32),
293 base::Bind(&MediaStreamCaptureIndicator::OnImageLoaded, 315 base::Bind(&MediaStreamCaptureIndicator::OnImageLoaded,
294 this, message)); 316 this, message));
295 } 317 }
296 318
297 void MediaStreamCaptureIndicator::OnImageLoaded( 319 void MediaStreamCaptureIndicator::OnImageLoaded(
298 const string16& message, 320 const string16& message,
299 const gfx::Image& image) { 321 const gfx::Image& image) {
300 if (!should_show_balloon_) 322 if (!should_show_balloon_ || !status_icon_)
301 return; 323 return;
302 324
303 const gfx::ImageSkia* image_skia = !image.IsEmpty() ? image.ToImageSkia() : 325 const gfx::ImageSkia* image_skia = !image.IsEmpty() ? image.ToImageSkia() :
304 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 326 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
305 IDR_APP_DEFAULT_ICON); 327 IDR_APP_DEFAULT_ICON);
306 status_icon_->DisplayBalloon(*image_skia, string16(), message); 328 status_icon_->DisplayBalloon(*image_skia, string16(), message);
307 } 329 }
308 330
309 void MediaStreamCaptureIndicator::Hide() { 331 void MediaStreamCaptureIndicator::MaybeDestroyStatusTrayIcon() {
310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
311 333
312 // Make sure images that finish loading don't cause a balloon to be shown. 334 // Make sure images that finish loading don't cause a balloon to be shown.
313 should_show_balloon_ = false; 335 should_show_balloon_ = false;
314 336
315 if (!status_icon_) 337 if (!status_icon_)
316 return; 338 return;
317 339
318 // If there is no browser process, we should not do anything. 340 // If there is no browser process, we should not do anything.
319 if (!g_browser_process) 341 if (!g_browser_process)
320 return; 342 return;
321 343
322 StatusTray* status_tray = g_browser_process->status_tray(); 344 StatusTray* status_tray = g_browser_process->status_tray();
323 if (status_tray != NULL) { 345 if (status_tray != NULL) {
324 status_tray->RemoveStatusIcon(status_icon_); 346 status_tray->RemoveStatusIcon(status_icon_);
325 status_icon_ = NULL; 347 status_icon_ = NULL;
326 } 348 }
327 } 349 }
328 350
329 void MediaStreamCaptureIndicator::UpdateStatusTrayIconContextMenu() { 351 void MediaStreamCaptureIndicator::UpdateStatusTrayIconContextMenu() {
330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
331 scoped_ptr<ui::SimpleMenuModel> menu(new ui::SimpleMenuModel(this)); 353 scoped_ptr<ui::SimpleMenuModel> menu(new ui::SimpleMenuModel(this));
332 354
333 bool audio = false; 355 bool audio = false;
334 bool video = false; 356 bool video = false;
335 int command_id = IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_FIRST; 357 int command_id = IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_FIRST;
336 for (CaptureDeviceTabs::iterator iter = tabs_.begin(); 358 command_map_.clear();
337 iter != tabs_.end();) { 359 for (UsageMap::const_iterator iter = usage_map_.begin();
338 string16 tab_title = GetTitle(iter->render_process_id, 360 iter != usage_map_.end(); ++iter) {
339 iter->render_view_id); 361 WebContents* const web_contents = iter->first;
340 if (tab_title.empty()) { 362 command_map_.push_back(web_contents);
341 // Delete the entry since the tab has gone away. 363 menu->AddItem(command_id, GetTitle(web_contents));
342 iter = tabs_.erase(iter);
343 continue;
344 }
345 364
346 // Check if any audio and video devices have been used. 365 // Check if any audio and video devices have been used.
347 audio = audio || iter->audio_ref_count > 0; 366 const CaptureDeviceUsage& usage = *iter->second;
348 video = video || iter->video_ref_count > 0; 367 audio = audio || usage.IsCapturingAudio();
349 368 video = video || usage.IsCapturingVideo();
350 menu->AddItem(command_id, tab_title);
351 369
352 // If reaching the maximum number, no more item will be added to the menu. 370 // If reaching the maximum number, no more item will be added to the menu.
353 if (command_id == IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_LAST) 371 if (command_id == IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_LAST)
354 break; 372 break;
355
356 ++command_id; 373 ++command_id;
357 ++iter;
358 } 374 }
359 375
360 if (!audio && !video) { 376 if (!audio && !video) {
361 Hide(); 377 MaybeDestroyStatusTrayIcon();
362 return; 378 return;
363 } 379 }
364 380
365 // The icon will take the ownership of the passed context menu. 381 // The icon will take the ownership of the passed context menu.
366 status_icon_->SetContextMenu(menu.release()); 382 MaybeCreateStatusTrayIcon();
367 UpdateStatusTrayIconDisplay(audio, video); 383 if (status_icon_) {
384 status_icon_->SetContextMenu(menu.release());
385 UpdateStatusTrayIconDisplay(audio, video);
386 }
368 } 387 }
369 388
370 void MediaStreamCaptureIndicator::UpdateStatusTrayIconDisplay( 389 void MediaStreamCaptureIndicator::UpdateStatusTrayIconDisplay(
371 bool audio, bool video) { 390 bool audio, bool video) {
372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
373 DCHECK(audio || video); 392 DCHECK(audio || video);
393 DCHECK(status_icon_);
374 int message_id = 0; 394 int message_id = 0;
375 if (audio && video) { 395 if (audio && video) {
376 message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_AND_VIDEO; 396 message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_AND_VIDEO;
377 status_icon_->SetImage(*camera_image_); 397 status_icon_->SetImage(*camera_image_);
378 } else if (audio && !video) { 398 } else if (audio && !video) {
379 message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_ONLY; 399 message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_ONLY;
380 status_icon_->SetImage(*mic_image_); 400 status_icon_->SetImage(*mic_image_);
381 } else if (!audio && video) { 401 } else if (!audio && video) {
382 message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_VIDEO_ONLY; 402 message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_VIDEO_ONLY;
383 status_icon_->SetImage(*camera_image_); 403 status_icon_->SetImage(*camera_image_);
384 } 404 }
385 405
386 status_icon_->SetToolTip(l10n_util::GetStringFUTF16( 406 status_icon_->SetToolTip(l10n_util::GetStringFUTF16(
387 message_id, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); 407 message_id, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
388 } 408 }
389 409
390 void MediaStreamCaptureIndicator::AddCaptureDeviceTab( 410 content::WebContents* MediaStreamCaptureIndicator::LookUpByKnownAlias(
411 int render_process_id, int render_view_id) const {
412 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
413
414 WebContents* result =
415 tab_util::GetWebContentsByID(render_process_id, render_view_id);
416 if (!result) {
417 const RenderViewIDs key(render_process_id, render_view_id);
418 AliasMap::const_iterator it = aliases_.find(key);
419 if (it != aliases_.end())
justinlin 2013/01/23 19:30:43 DCHECK this? Seems like all lookups should resolve
miu 2013/01/23 20:33:28 Not necessarily. Example: When this method is cal
420 result = it->second;
421 }
422 return result;
423 }
424
425 void MediaStreamCaptureIndicator::AddCaptureDevices(
391 int render_process_id, 426 int render_process_id,
392 int render_view_id, 427 int render_view_id,
393 const content::MediaStreamDevices& devices) { 428 const content::MediaStreamDevices& devices) {
394 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 429 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
395 430
396 WebContents* web_contents = tab_util::GetWebContentsByID(render_process_id, 431 WebContents* const web_contents =
397 render_view_id); 432 LookUpByKnownAlias(render_process_id, render_view_id);
398 if (!web_contents) 433 if (!web_contents)
399 return; 434 return;
400 435
401 CaptureDeviceTabs::iterator iter = std::find_if( 436 // Increase the usage ref-counts.
402 tabs_.begin(), tabs_.end(), TabEquals(web_contents, 437 int num_audio, num_video, num_mirroring;
403 render_process_id, 438 CountDevices(devices, &num_audio, &num_video, &num_mirroring);
404 render_view_id)); 439 DCHECK(num_audio > 0 || num_video > 0 || num_mirroring > 0);
405 if (iter == tabs_.end()) { 440 CaptureDeviceUsage*& usage = usage_map_[web_contents];
406 tabs_.push_back(CaptureDeviceTab(web_contents, 441 if (!usage)
407 render_process_id, 442 usage = new CaptureDeviceUsage();
408 render_view_id)); 443 usage->TallyUsage(num_audio, num_video, num_mirroring);
409 iter = tabs_.end() - 1;
410 }
411 444
412 bool audio = false; 445 // Keep track of the IDs as a known alias to the WebContents instance.
413 bool video = false; 446 const AliasMap::iterator insert_it = aliases_.insert(
414 bool tab_capture = false; 447 make_pair(RenderViewIDs(render_process_id, render_view_id),
415 content::MediaStreamDevices::const_iterator dev = devices.begin(); 448 web_contents)).first;
416 for (; dev != devices.end(); ++dev) { 449 DCHECK_EQ(web_contents, insert_it->second)
417 if (dev->type == content::MEDIA_TAB_AUDIO_CAPTURE || 450 << "BUG: IDs refer to two different WebContents instances.";
418 dev->type == content::MEDIA_TAB_VIDEO_CAPTURE) {
419 ++iter->tab_capture_ref_count;
420 tab_capture = true;
421 } else if (content::IsAudioMediaType(dev->type)) {
422 ++iter->audio_ref_count;
423 audio = true;
424 } else if (content::IsVideoMediaType(dev->type)) {
425 ++iter->video_ref_count;
426 video = true;
427 } else {
428 NOTIMPLEMENTED();
429 }
430 }
431 451
432 DCHECK(web_contents);
433 web_contents->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB); 452 web_contents->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
434 453
435 // Don't use desktop notifications for tab capture. We use a favicon 454 // Don't use desktop notifications for tab capture. We use a favicon
436 // glow notification instead. 455 // glow notification instead.
437 if (!status_icon_ || tab_capture) 456 if (num_mirroring > 0 && num_audio == 0 && num_video == 0)
438 return; 457 return;
439 458
440 UpdateStatusTrayIconContextMenu(); 459 UpdateStatusTrayIconContextMenu();
441 ShowBalloon(render_process_id, render_view_id, audio, video); 460 ShowBalloon(web_contents, num_audio > 0, num_video > 0);
442 } 461 }
443 462
444 void MediaStreamCaptureIndicator::RemoveCaptureDeviceTab( 463 void MediaStreamCaptureIndicator::RemoveCaptureDevices(
445 int render_process_id, 464 int render_process_id,
446 int render_view_id, 465 int render_view_id,
447 const content::MediaStreamDevices& devices) { 466 const content::MediaStreamDevices& devices) {
448 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 467 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
449 WebContents* web_contents = tab_util::GetWebContentsByID(render_process_id,
450 render_view_id);
451 CaptureDeviceTabs::iterator iter = std::find_if(
452 tabs_.begin(), tabs_.end(), TabEquals(web_contents,
453 render_process_id,
454 render_view_id));
455 468
456 if (iter != tabs_.end()) { 469 WebContents* const web_contents =
457 content::MediaStreamDevices::const_iterator dev = devices.begin(); 470 LookUpByKnownAlias(render_process_id, render_view_id);
458 for (; dev != devices.end(); ++dev) { 471 if (!web_contents)
459 if (dev->type == content::MEDIA_TAB_AUDIO_CAPTURE || 472 return;
460 dev->type == content::MEDIA_TAB_VIDEO_CAPTURE) { 473
461 --iter->tab_capture_ref_count; 474 // Decrease the usage ref-counts.
462 } else if (content::IsAudioMediaType(dev->type)) { 475 int num_audio, num_video, num_mirroring;
justinlin 2013/01/23 19:30:43 nit: I think the style guide specifies that they e
miu 2013/01/23 20:33:28 Done.
463 --iter->audio_ref_count; 476 CountDevices(devices, &num_audio, &num_video, &num_mirroring);
464 } else if (content::IsVideoMediaType(dev->type)) { 477 CaptureDeviceUsage* const usage = usage_map_[web_contents];
465 --iter->video_ref_count; 478 DCHECK(usage);
479 usage->TallyUsage(-num_audio, -num_video, -num_mirroring);
480
481 // Remove the usage and alias mappings if all the devices have been closed.
482 if (!usage->IsCapturingAudio() && !usage->IsCapturingVideo() &&
483 !usage->IsMirroring()) {
484 for (AliasMap::iterator alias_it = aliases_.begin();
485 alias_it != aliases_.end(); ) {
486 if (alias_it->second == web_contents) {
487 aliases_.erase(alias_it++);
466 } else { 488 } else {
467 NOTIMPLEMENTED(); 489 ++alias_it;
468 } 490 }
469
470 DCHECK_GE(iter->audio_ref_count, 0);
471 DCHECK_GE(iter->video_ref_count, 0);
472 } 491 }
473 492 delete usage;
474 // Remove the tab if all the devices have been closed. 493 usage_map_.erase(web_contents);
475 if (iter->audio_ref_count == 0 && iter->video_ref_count == 0 &&
476 iter->tab_capture_ref_count == 0)
477 tabs_.erase(iter);
478 } 494 }
479 495
480 if (web_contents) 496 web_contents->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
481 web_contents->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
482
483 if (!status_icon_)
484 return;
485 497
486 UpdateStatusTrayIconContextMenu(); 498 UpdateStatusTrayIconContextMenu();
487 } 499 }
488 500
489 bool MediaStreamCaptureIndicator::IsProcessCapturing(int render_process_id, 501 bool MediaStreamCaptureIndicator::IsRenderViewCapturing(
490 int render_view_id) const { 502 int render_process_id, int render_view_id) const {
491 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 503 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
492 WebContents* web_contents = tab_util::GetWebContentsByID(render_process_id, 504
493 render_view_id); 505 WebContents* const web_contents =
506 LookUpByKnownAlias(render_process_id, render_view_id);
494 if (!web_contents) 507 if (!web_contents)
495 return false; 508 return false;
496 509
497 CaptureDeviceTabs::const_iterator iter = std::find_if( 510 UsageMap::const_iterator it = usage_map_.find(web_contents);
498 tabs_.begin(), tabs_.end(), TabEquals(web_contents, 511 return (it != usage_map_.end() &&
499 render_process_id, 512 (it->second->IsCapturingAudio() || it->second->IsCapturingVideo()));
500 render_view_id));
501 if (iter == tabs_.end())
502 return false;
503 return (iter->audio_ref_count > 0 || iter->video_ref_count > 0);
504 } 513 }
505 514
506 bool MediaStreamCaptureIndicator::IsProcessCapturingTab( 515 bool MediaStreamCaptureIndicator::IsRenderViewMirroring(
507 int render_process_id, int render_view_id) const { 516 int render_process_id, int render_view_id) const {
508 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 517 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
509 WebContents* web_contents = tab_util::GetWebContentsByID(render_process_id, 518
510 render_view_id); 519 WebContents* const web_contents =
520 LookUpByKnownAlias(render_process_id, render_view_id);
511 if (!web_contents) 521 if (!web_contents)
512 return false; 522 return false;
513 523
514 CaptureDeviceTabs::const_iterator iter = std::find_if( 524 UsageMap::const_iterator it = usage_map_.find(web_contents);
515 tabs_.begin(), tabs_.end(), TabEquals(web_contents, 525 return it != usage_map_.end() && it->second->IsMirroring();
516 render_process_id,
517 render_view_id));
518 if (iter == tabs_.end())
519 return false;
520 return (iter->tab_capture_ref_count > 0);
521 } 526 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698