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

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: Fix issue with de-ref'ing WebContents after destruction. 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(content::WebContents* web_contents);
119 114
120 bool MediaStreamCaptureIndicator::TabEquals::operator() ( 115 bool IsWebContentsDestroyed() const { return web_contents() == NULL; }
justinlin 2013/01/28 22:31:21 Hmm, is this really needed? I think callers just c
miu 2013/01/28 23:57:13 Seemed cleaner. Some reasons: 1) Callers already
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 by |inc_amount| (must be +1 for increment or -1 for
122 // decrement) based on the type of each device provided. In the increment
123 // case, the return value is the message ID for the balloon body to show, or
124 // zero if the balloon should not be shown.
125 int TallyUsage(const content::MediaStreamDevices& devices, int inc_amount);
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 content::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(
justinlin 2013/01/28 22:31:21 This could probably use a better name since it ret
142 const content::MediaStreamDevices& devices, int inc_amount) {
justinlin 2013/01/28 22:31:21 nit: Make inc_amount a bool?
miu 2013/01/28 23:57:13 Done.
143 DCHECK(inc_amount == 1 || inc_amount == -1);
144
145 bool incremented_audio_count = false;
146 bool incremented_video_count = false;
147 for (content::MediaStreamDevices::const_iterator it = devices.begin();
148 it != devices.end(); ++it) {
149 if (it->type == content::MEDIA_TAB_AUDIO_CAPTURE ||
150 it->type == content::MEDIA_TAB_VIDEO_CAPTURE) {
151 mirroring_ref_count_ += inc_amount;
152 } else if (content::IsAudioMediaType(it->type)) {
153 audio_ref_count_ += inc_amount;
154 incremented_audio_count = (inc_amount > 0);
155 } else if (content::IsVideoMediaType(it->type)) {
156 video_ref_count_ += inc_amount;
157 incremented_video_count = (inc_amount > 0);
158 } else {
159 NOTIMPLEMENTED();
160 }
161 }
162
163 DCHECK_LE(0, audio_ref_count_);
164 DCHECK_LE(0, video_ref_count_);
165 DCHECK_LE(0, mirroring_ref_count_);
166
167 if (incremented_audio_count && incremented_video_count)
168 return IDS_MEDIA_STREAM_STATUS_TRAY_BALLOON_BODY_AUDIO_AND_VIDEO;
169 else if (incremented_audio_count)
170 return IDS_MEDIA_STREAM_STATUS_TRAY_BALLOON_BODY_AUDIO_ONLY;
171 else if (incremented_video_count)
172 return IDS_MEDIA_STREAM_STATUS_TRAY_BALLOON_BODY_VIDEO_ONLY;
173 else
174 return 0;
125 } 175 }
126 176
127 MediaStreamCaptureIndicator::MediaStreamCaptureIndicator() 177 MediaStreamCaptureIndicator::MediaStreamCaptureIndicator()
128 : status_icon_(NULL), 178 : status_icon_(NULL),
129 mic_image_(NULL), 179 mic_image_(NULL),
130 camera_image_(NULL), 180 camera_image_(NULL),
131 balloon_image_(NULL), 181 balloon_image_(NULL),
132 should_show_balloon_(false) { 182 should_show_balloon_(false) {
133 } 183 }
134 184
135 MediaStreamCaptureIndicator::~MediaStreamCaptureIndicator() { 185 MediaStreamCaptureIndicator::~MediaStreamCaptureIndicator() {
136 // The user is responsible for cleaning up by closing all the opened devices. 186 // The user is responsible for cleaning up by reporting the closure of any
137 DCHECK(tabs_.empty()); 187 // opened devices.
188 DCHECK(usage_map_.empty());
138 } 189 }
139 190
140 bool MediaStreamCaptureIndicator::IsCommandIdChecked( 191 bool MediaStreamCaptureIndicator::IsCommandIdChecked(
141 int command_id) const { 192 int command_id) const {
142 NOTIMPLEMENTED() << "There are no checked items in the MediaStream menu."; 193 NOTIMPLEMENTED() << "There are no checked items in the MediaStream menu.";
143 return false; 194 return false;
144 } 195 }
145 196
146 bool MediaStreamCaptureIndicator::IsCommandIdEnabled( 197 bool MediaStreamCaptureIndicator::IsCommandIdEnabled(
147 int command_id) const { 198 int command_id) const {
148 return command_id != IDC_MinimumLabelValue; 199 return command_id != IDC_MinimumLabelValue;
149 } 200 }
150 201
151 bool MediaStreamCaptureIndicator::GetAcceleratorForCommandId( 202 bool MediaStreamCaptureIndicator::GetAcceleratorForCommandId(
152 int command_id, ui::Accelerator* accelerator) { 203 int command_id, ui::Accelerator* accelerator) {
153 // No accelerators for status icon context menu. 204 // No accelerators for status icon context menu.
154 return false; 205 return false;
155 } 206 }
156 207
157 void MediaStreamCaptureIndicator::ExecuteCommand(int command_id) { 208 void MediaStreamCaptureIndicator::ExecuteCommand(int command_id) {
158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 209 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
159 DCHECK(command_id >= IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_FIRST && 210
160 command_id <= IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_LAST); 211 const int index =
161 int index = command_id - IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_FIRST; 212 command_id - IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_FIRST;
162 WebContents* web_content = tab_util::GetWebContentsByID( 213 DCHECK_LE(0, index);
163 tabs_[index].render_process_id, tabs_[index].render_view_id); 214 DCHECK_GT(static_cast<int>(command_map_.size()), index);
164 DCHECK(web_content); 215 WebContents* const web_contents = command_map_[index];
165 if (!web_content) { 216 UsageMap::const_iterator it = usage_map_.find(web_contents);
166 NOTREACHED(); 217 if (it == usage_map_.end() || it->second->IsWebContentsDestroyed())
167 return; 218 return;
168 } 219 web_contents->GetDelegate()->ActivateContents(web_contents);
169
170 web_content->GetDelegate()->ActivateContents(web_content);
171 } 220 }
172 221
173 void MediaStreamCaptureIndicator::CaptureDevicesOpened( 222 void MediaStreamCaptureIndicator::CaptureDevicesOpened(
174 int render_process_id, 223 int render_process_id,
175 int render_view_id, 224 int render_view_id,
176 const content::MediaStreamDevices& devices) { 225 const content::MediaStreamDevices& devices) {
177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
178 DCHECK(!devices.empty()); 227 DCHECK(!devices.empty());
179 228
180 BrowserThread::PostTask( 229 BrowserThread::PostTask(
(...skipping 14 matching lines...) Expand all
195 base::Bind(&MediaStreamCaptureIndicator::DoDevicesClosedOnUIThread, 244 base::Bind(&MediaStreamCaptureIndicator::DoDevicesClosedOnUIThread,
196 this, render_process_id, render_view_id, devices)); 245 this, render_process_id, render_view_id, devices));
197 } 246 }
198 247
199 void MediaStreamCaptureIndicator::DoDevicesOpenedOnUIThread( 248 void MediaStreamCaptureIndicator::DoDevicesOpenedOnUIThread(
200 int render_process_id, 249 int render_process_id,
201 int render_view_id, 250 int render_view_id,
202 const content::MediaStreamDevices& devices) { 251 const content::MediaStreamDevices& devices) {
203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 252 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
204 253
205 CreateStatusTray(); 254 AddCaptureDevices(render_process_id, render_view_id, devices);
206
207 AddCaptureDeviceTab(render_process_id, render_view_id, devices);
208 } 255 }
209 256
210 void MediaStreamCaptureIndicator::DoDevicesClosedOnUIThread( 257 void MediaStreamCaptureIndicator::DoDevicesClosedOnUIThread(
211 int render_process_id, 258 int render_process_id,
212 int render_view_id, 259 int render_view_id,
213 const content::MediaStreamDevices& devices) { 260 const content::MediaStreamDevices& devices) {
214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
215 262
216 RemoveCaptureDeviceTab(render_process_id, render_view_id, devices); 263 RemoveCaptureDevices(render_process_id, render_view_id, devices);
217 } 264 }
218 265
219 void MediaStreamCaptureIndicator::CreateStatusTray() { 266 void MediaStreamCaptureIndicator::MaybeCreateStatusTrayIcon() {
220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
221 if (status_icon_) 268 if (status_icon_)
222 return; 269 return;
223 270
224 // If there is no browser process, we should not create the status tray. 271 // If there is no browser process, we should not create the status tray.
225 if (!g_browser_process) 272 if (!g_browser_process)
226 return; 273 return;
227 274
228 StatusTray* status_tray = g_browser_process->status_tray(); 275 StatusTray* status_tray = g_browser_process->status_tray();
229 if (!status_tray) 276 if (!status_tray)
(...skipping 17 matching lines...) Expand all
247 if (!balloon_image_) { 294 if (!balloon_image_) {
248 balloon_image_ = ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 295 balloon_image_ = ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
249 IDR_PRODUCT_LOGO_32); 296 IDR_PRODUCT_LOGO_32);
250 } 297 }
251 DCHECK(mic_image_); 298 DCHECK(mic_image_);
252 DCHECK(camera_image_); 299 DCHECK(camera_image_);
253 DCHECK(balloon_image_); 300 DCHECK(balloon_image_);
254 } 301 }
255 302
256 void MediaStreamCaptureIndicator::ShowBalloon( 303 void MediaStreamCaptureIndicator::ShowBalloon(
257 int render_process_id, 304 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)); 305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
262 DCHECK(audio || video); 306 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 307
270 // Only show the balloon for extensions. 308 // Only show the balloon for extensions.
271 const extensions::Extension* extension = 309 const extensions::Extension* const extension = GetExtension(web_contents);
272 GetExtension(render_process_id, render_view_id);
273 if (!extension) { 310 if (!extension) {
274 DVLOG(1) << "Balloon is shown only for extensions"; 311 DVLOG(1) << "Balloon is shown only for extensions";
275 return; 312 return;
276 } 313 }
277 314
278 string16 message = 315 string16 message =
279 l10n_util::GetStringFUTF16(message_id, 316 l10n_util::GetStringFUTF16(balloon_body_message_id,
280 UTF8ToUTF16(extension->name())); 317 UTF8ToUTF16(extension->name()));
281
282 WebContents* web_contents = tab_util::GetWebContentsByID(
283 render_process_id, render_view_id);
284
285 Profile* profile = 318 Profile* profile =
286 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 319 Profile::FromBrowserContext(web_contents->GetBrowserContext());
287 320
288 should_show_balloon_ = true; 321 should_show_balloon_ = true;
289 extensions::ImageLoader::Get(profile)->LoadImageAsync( 322 extensions::ImageLoader::Get(profile)->LoadImageAsync(
290 extension, 323 extension,
291 extension->GetIconResource(32, ExtensionIconSet::MATCH_BIGGER), 324 extension->GetIconResource(32, ExtensionIconSet::MATCH_BIGGER),
292 gfx::Size(32, 32), 325 gfx::Size(32, 32),
293 base::Bind(&MediaStreamCaptureIndicator::OnImageLoaded, 326 base::Bind(&MediaStreamCaptureIndicator::OnImageLoaded,
294 this, message)); 327 this, message));
295 } 328 }
296 329
297 void MediaStreamCaptureIndicator::OnImageLoaded( 330 void MediaStreamCaptureIndicator::OnImageLoaded(
298 const string16& message, 331 const string16& message,
299 const gfx::Image& image) { 332 const gfx::Image& image) {
300 if (!should_show_balloon_) 333 if (!should_show_balloon_ || !status_icon_)
301 return; 334 return;
302 335
303 const gfx::ImageSkia* image_skia = !image.IsEmpty() ? image.ToImageSkia() : 336 const gfx::ImageSkia* image_skia = !image.IsEmpty() ? image.ToImageSkia() :
304 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 337 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
305 IDR_APP_DEFAULT_ICON); 338 IDR_APP_DEFAULT_ICON);
306 status_icon_->DisplayBalloon(*image_skia, string16(), message); 339 status_icon_->DisplayBalloon(*image_skia, string16(), message);
307 } 340 }
308 341
309 void MediaStreamCaptureIndicator::Hide() { 342 void MediaStreamCaptureIndicator::MaybeDestroyStatusTrayIcon() {
310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 343 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
311 344
312 // Make sure images that finish loading don't cause a balloon to be shown. 345 // Make sure images that finish loading don't cause a balloon to be shown.
313 should_show_balloon_ = false; 346 should_show_balloon_ = false;
314 347
315 if (!status_icon_) 348 if (!status_icon_)
316 return; 349 return;
317 350
318 // If there is no browser process, we should not do anything. 351 // If there is no browser process, we should not do anything.
319 if (!g_browser_process) 352 if (!g_browser_process)
320 return; 353 return;
321 354
322 StatusTray* status_tray = g_browser_process->status_tray(); 355 StatusTray* status_tray = g_browser_process->status_tray();
323 if (status_tray != NULL) { 356 if (status_tray != NULL) {
324 status_tray->RemoveStatusIcon(status_icon_); 357 status_tray->RemoveStatusIcon(status_icon_);
325 status_icon_ = NULL; 358 status_icon_ = NULL;
326 } 359 }
327 } 360 }
328 361
329 void MediaStreamCaptureIndicator::UpdateStatusTrayIconContextMenu() { 362 void MediaStreamCaptureIndicator::UpdateStatusTrayIconContextMenu() {
330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
331 scoped_ptr<ui::SimpleMenuModel> menu(new ui::SimpleMenuModel(this)); 364 scoped_ptr<ui::SimpleMenuModel> menu(new ui::SimpleMenuModel(this));
332 365
333 bool audio = false; 366 bool audio = false;
334 bool video = false; 367 bool video = false;
335 int command_id = IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_FIRST; 368 int command_id = IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_FIRST;
336 for (CaptureDeviceTabs::iterator iter = tabs_.begin(); 369 command_map_.clear();
337 iter != tabs_.end();) { 370 for (UsageMap::const_iterator iter = usage_map_.begin();
338 string16 tab_title = GetTitle(iter->render_process_id, 371 iter != usage_map_.end(); ++iter) {
339 iter->render_view_id); 372 // Check if any audio and video devices have been used.
340 if (tab_title.empty()) { 373 const WebContentsDeviceUsage& usage = *iter->second;
341 // Delete the entry since the tab has gone away. 374 if (usage.IsWebContentsDestroyed() ||
342 iter = tabs_.erase(iter); 375 (!usage.IsCapturingAudio() && !usage.IsCapturingVideo())) {
343 continue; 376 continue;
344 } 377 }
378 audio = audio || usage.IsCapturingAudio();
379 video = video || usage.IsCapturingVideo();
345 380
346 // Check if any audio and video devices have been used. 381 WebContents* const web_contents = iter->first;
347 audio = audio || iter->audio_ref_count > 0; 382 command_map_.push_back(web_contents);
348 video = video || iter->video_ref_count > 0; 383 menu->AddItem(command_id, GetTitle(web_contents));
349
350 menu->AddItem(command_id, tab_title);
351 384
352 // If reaching the maximum number, no more item will be added to the menu. 385 // 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) 386 if (command_id == IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_LAST)
354 break; 387 break;
355
356 ++command_id; 388 ++command_id;
357 ++iter;
358 } 389 }
359 390
360 if (!audio && !video) { 391 if (command_map_.empty()) {
361 Hide(); 392 MaybeDestroyStatusTrayIcon();
362 return; 393 return;
363 } 394 }
364 395
365 // The icon will take the ownership of the passed context menu. 396 // The icon will take the ownership of the passed context menu.
366 status_icon_->SetContextMenu(menu.release()); 397 MaybeCreateStatusTrayIcon();
367 UpdateStatusTrayIconDisplay(audio, video); 398 if (status_icon_) {
399 status_icon_->SetContextMenu(menu.release());
400 UpdateStatusTrayIconDisplay(audio, video);
401 }
368 } 402 }
369 403
370 void MediaStreamCaptureIndicator::UpdateStatusTrayIconDisplay( 404 void MediaStreamCaptureIndicator::UpdateStatusTrayIconDisplay(
371 bool audio, bool video) { 405 bool audio, bool video) {
372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 406 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
373 DCHECK(audio || video); 407 DCHECK(audio || video);
408 DCHECK(status_icon_);
374 int message_id = 0; 409 int message_id = 0;
375 if (audio && video) { 410 if (audio && video) {
376 message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_AND_VIDEO; 411 message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_AND_VIDEO;
377 status_icon_->SetImage(*camera_image_); 412 status_icon_->SetImage(*camera_image_);
378 } else if (audio && !video) { 413 } else if (audio && !video) {
379 message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_ONLY; 414 message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_ONLY;
380 status_icon_->SetImage(*mic_image_); 415 status_icon_->SetImage(*mic_image_);
381 } else if (!audio && video) { 416 } else if (!audio && video) {
382 message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_VIDEO_ONLY; 417 message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_VIDEO_ONLY;
383 status_icon_->SetImage(*camera_image_); 418 status_icon_->SetImage(*camera_image_);
384 } 419 }
385 420
386 status_icon_->SetToolTip(l10n_util::GetStringFUTF16( 421 status_icon_->SetToolTip(l10n_util::GetStringFUTF16(
387 message_id, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); 422 message_id, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
388 } 423 }
389 424
390 void MediaStreamCaptureIndicator::AddCaptureDeviceTab( 425 content::WebContents* MediaStreamCaptureIndicator::LookUpByKnownAlias(
426 int render_process_id, int render_view_id) const {
427 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
428
429 WebContents* result =
430 tab_util::GetWebContentsByID(render_process_id, render_view_id);
431 if (!result) {
432 const RenderViewIDs key(render_process_id, render_view_id);
433 AliasMap::const_iterator it = aliases_.find(key);
justinlin 2013/01/28 22:31:21 nit: make_pair? your preference.
434 if (it != aliases_.end())
435 result = it->second;
436 }
437 return result;
438 }
439
440 void MediaStreamCaptureIndicator::AddCaptureDevices(
391 int render_process_id, 441 int render_process_id,
392 int render_view_id, 442 int render_view_id,
393 const content::MediaStreamDevices& devices) { 443 const content::MediaStreamDevices& devices) {
394 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 444 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
395 445
396 WebContents* web_contents = tab_util::GetWebContentsByID(render_process_id, 446 WebContents* const web_contents =
397 render_view_id); 447 LookUpByKnownAlias(render_process_id, render_view_id);
398 if (!web_contents) 448 if (!web_contents)
399 return; 449 return;
400 450
401 CaptureDeviceTabs::iterator iter = std::find_if( 451 // Increase the usage ref-counts.
402 tabs_.begin(), tabs_.end(), TabEquals(web_contents, 452 WebContentsDeviceUsage*& usage = usage_map_[web_contents];
403 render_process_id, 453 if (!usage)
404 render_view_id)); 454 usage = new WebContentsDeviceUsage(web_contents);
405 if (iter == tabs_.end()) { 455 const int balloon_body_message_id = usage->TallyUsage(devices, +1);
406 tabs_.push_back(CaptureDeviceTab(web_contents,
407 render_process_id,
408 render_view_id));
409 iter = tabs_.end() - 1;
410 }
411 456
412 bool audio = false; 457 // Keep track of the IDs as a known alias to the WebContents instance.
413 bool video = false; 458 const AliasMap::iterator insert_it = aliases_.insert(
414 bool tab_capture = false; 459 make_pair(RenderViewIDs(render_process_id, render_view_id),
415 content::MediaStreamDevices::const_iterator dev = devices.begin(); 460 web_contents)).first;
416 for (; dev != devices.end(); ++dev) { 461 DCHECK_EQ(web_contents, insert_it->second)
417 if (dev->type == content::MEDIA_TAB_AUDIO_CAPTURE || 462 << "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 463
432 DCHECK(web_contents);
433 web_contents->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB); 464 web_contents->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
434 465
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(); 466 UpdateStatusTrayIconContextMenu();
441 ShowBalloon(render_process_id, render_view_id, audio, video); 467 if (balloon_body_message_id)
468 ShowBalloon(web_contents, balloon_body_message_id);
442 } 469 }
443 470
444 void MediaStreamCaptureIndicator::RemoveCaptureDeviceTab( 471 void MediaStreamCaptureIndicator::RemoveCaptureDevices(
445 int render_process_id, 472 int render_process_id,
446 int render_view_id, 473 int render_view_id,
447 const content::MediaStreamDevices& devices) { 474 const content::MediaStreamDevices& devices) {
448 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 475 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 476
456 if (iter != tabs_.end()) { 477 WebContents* const web_contents =
457 content::MediaStreamDevices::const_iterator dev = devices.begin(); 478 LookUpByKnownAlias(render_process_id, render_view_id);
458 for (; dev != devices.end(); ++dev) { 479 if (!web_contents)
459 if (dev->type == content::MEDIA_TAB_AUDIO_CAPTURE || 480 return;
460 dev->type == content::MEDIA_TAB_VIDEO_CAPTURE) { 481
461 --iter->tab_capture_ref_count; 482 // Decrease the usage ref-counts.
462 } else if (content::IsAudioMediaType(dev->type)) { 483 WebContentsDeviceUsage* const usage = usage_map_[web_contents];
463 --iter->audio_ref_count; 484 DCHECK(usage);
464 } else if (content::IsVideoMediaType(dev->type)) { 485 usage->TallyUsage(devices, -1);
465 --iter->video_ref_count; 486 const bool should_notify_web_contents = !usage->IsWebContentsDestroyed();
487
488 // Remove the usage and alias mappings if all the devices have been closed.
489 if (!usage->IsCapturingAudio() && !usage->IsCapturingVideo() &&
490 !usage->IsMirroring()) {
491 for (AliasMap::iterator alias_it = aliases_.begin();
492 alias_it != aliases_.end(); ) {
493 if (alias_it->second == web_contents) {
justinlin 2013/01/28 22:31:21 nit: no braces
miu 2013/01/28 23:57:13 Done.
494 aliases_.erase(alias_it++);
466 } else { 495 } else {
467 NOTIMPLEMENTED(); 496 ++alias_it;
468 } 497 }
469
470 DCHECK_GE(iter->audio_ref_count, 0);
471 DCHECK_GE(iter->video_ref_count, 0);
472 } 498 }
473 499 delete usage;
474 // Remove the tab if all the devices have been closed. 500 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 } 501 }
479 502
480 if (web_contents) 503 if (should_notify_web_contents)
481 web_contents->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB); 504 web_contents->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
482 505
483 if (!status_icon_)
484 return;
485
486 UpdateStatusTrayIconContextMenu(); 506 UpdateStatusTrayIconContextMenu();
487 } 507 }
488 508
489 bool MediaStreamCaptureIndicator::IsProcessCapturing(int render_process_id, 509 bool MediaStreamCaptureIndicator::IsCapturingUserMedia(
490 int render_view_id) const { 510 int render_process_id, int render_view_id) const {
491 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 511 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
492 WebContents* web_contents = tab_util::GetWebContentsByID(render_process_id, 512
493 render_view_id); 513 WebContents* const web_contents =
514 LookUpByKnownAlias(render_process_id, render_view_id);
494 if (!web_contents) 515 if (!web_contents)
495 return false; 516 return false;
496 517
497 CaptureDeviceTabs::const_iterator iter = std::find_if( 518 UsageMap::const_iterator it = usage_map_.find(web_contents);
498 tabs_.begin(), tabs_.end(), TabEquals(web_contents, 519 return (it != usage_map_.end() &&
499 render_process_id, 520 (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 } 521 }
505 522
506 bool MediaStreamCaptureIndicator::IsProcessCapturingTab( 523 bool MediaStreamCaptureIndicator::IsBeingMirrored(
507 int render_process_id, int render_view_id) const { 524 int render_process_id, int render_view_id) const {
508 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 525 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
509 WebContents* web_contents = tab_util::GetWebContentsByID(render_process_id, 526
510 render_view_id); 527 WebContents* const web_contents =
528 LookUpByKnownAlias(render_process_id, render_view_id);
511 if (!web_contents) 529 if (!web_contents)
512 return false; 530 return false;
513 531
514 CaptureDeviceTabs::const_iterator iter = std::find_if( 532 UsageMap::const_iterator it = usage_map_.find(web_contents);
515 tabs_.begin(), tabs_.end(), TabEquals(web_contents, 533 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 } 534 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698