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

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

Powered by Google App Engine
This is Rietveld 408576698