Index: chrome/browser/media/media_stream_capture_indicator.cc |
diff --git a/chrome/browser/media/media_stream_capture_indicator.cc b/chrome/browser/media/media_stream_capture_indicator.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..64cc0c9d537255202802a5a83fd8831409fcb583 |
--- /dev/null |
+++ b/chrome/browser/media/media_stream_capture_indicator.cc |
@@ -0,0 +1,312 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/media/media_stream_capture_indicator.h" |
+ |
+#include "base/bind.h" |
+#include "base/i18n/rtl.h" |
+#include "base/utf_string_conversions.h" |
+#include "chrome/app/chrome_command_ids.h" |
+#include "chrome/browser/browser_process.h" |
+#include "chrome/browser/status_icons/status_icon.h" |
+#include "chrome/browser/status_icons/status_tray.h" |
+#include "chrome/browser/tab_contents/tab_util.h" |
+#include "content/public/browser/browser_thread.h" |
+#include "content/public/browser/web_contents.h" |
+#include "grit/chromium_strings.h" |
+#include "grit/generated_resources.h" |
+#include "grit/theme_resources.h" |
+#include "ui/base/l10n/l10n_util.h" |
+#include "ui/base/resource/resource_bundle.h" |
+ |
+using content::BrowserThread; |
+using content::WebContents; |
+ |
+MediaStreamCaptureIndicator::TabEquals::TabEquals( |
+ int render_process_id, |
+ int render_view_id, |
+ content::MediaStreamDeviceType type) |
+ : render_process_id_(render_process_id), |
+ render_view_id_(render_view_id), |
+ type_(type) {} |
+ |
+MediaStreamCaptureIndicator::TabEquals::TabEquals(int render_process_id, |
+ int render_view_id) |
+ : render_process_id_(render_process_id), |
+ render_view_id_(render_view_id), |
+ type_(content::MEDIA_STREAM_DEVICE_TYPE_NO_SERVICE) {} |
+ |
+bool MediaStreamCaptureIndicator::TabEquals::operator() ( |
+ const MediaStreamCaptureIndicator::CaptureDeviceTab& tab) { |
+ if (type_ == content::MEDIA_STREAM_DEVICE_TYPE_NO_SERVICE) { |
+ return (render_process_id_ == tab.render_process_id && |
+ render_view_id_ == tab.render_view_id); |
+ } else { |
+ return (render_process_id_ == tab.render_process_id && |
+ render_view_id_ == tab.render_view_id && |
+ type_ == tab.type); |
+ } |
+} |
+ |
+MediaStreamCaptureIndicator::MediaStreamCaptureIndicator() |
+ : status_icon_(NULL) { |
+} |
+ |
+MediaStreamCaptureIndicator::~MediaStreamCaptureIndicator() { |
+ // The user is responsible for cleaning up by closing all the opened devices. |
+ DCHECK(tabs_.empty()); |
+} |
+ |
+bool MediaStreamCaptureIndicator::IsCommandIdChecked( |
+ int command_id) const { |
+ NOTIMPLEMENTED() << "There are no checked items in the MediaStream menu."; |
+ return false; |
+} |
+ |
+bool MediaStreamCaptureIndicator::IsCommandIdEnabled( |
+ int command_id) const { |
+ return command_id != IDC_MinimumLabelValue; |
+} |
+ |
+bool MediaStreamCaptureIndicator::GetAcceleratorForCommandId( |
+ int command_id, ui::Accelerator* accelerator) { |
+ // No accelerators for status icon context menu. |
+ return false; |
+} |
+ |
+void MediaStreamCaptureIndicator::ExecuteCommand(int command_id) { |
+ // TODO(xians) : Implement all the following execute command function. |
+ switch (command_id) { |
+ case IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_FIRST: |
+ break; |
+ default: |
+ NOTREACHED(); |
+ break; |
+ } |
+} |
+ |
+void MediaStreamCaptureIndicator::CaptureDevicesOpened( |
+ int render_process_id, |
+ int render_view_id, |
+ const content::MediaStreamDevices& devices) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ DCHECK(!devices.empty()); |
+ |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, FROM_HERE, |
+ base::Bind(&MediaStreamCaptureIndicator::DoDevicesOpenedOnUIThread, |
+ this, render_process_id, render_view_id, devices)); |
+} |
+ |
+void MediaStreamCaptureIndicator::CaptureDevicesClosed( |
+ int render_process_id, |
+ int render_view_id, |
+ const content::MediaStreamDevices& devices) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ DCHECK(!devices.empty()); |
+ |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, FROM_HERE, |
+ base::Bind(&MediaStreamCaptureIndicator::DoDevicesClosedOnUIThread, |
+ this, render_process_id, render_view_id, devices)); |
+} |
+ |
+void MediaStreamCaptureIndicator::DoDevicesOpenedOnUIThread( |
+ int render_process_id, |
+ int render_view_id, |
+ const content::MediaStreamDevices& devices) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ CreateStatusTray(); |
+ |
+ // If we don't have a status icon or one could not be created successfully, |
+ // then no need to continue. |
+ if (!status_icon_) |
+ return; |
+ |
+ AddCaptureDeviceTab(render_process_id, render_view_id, devices); |
+ |
+ ShowBalloon(render_process_id, render_view_id, devices); |
+} |
+ |
+void MediaStreamCaptureIndicator::DoDevicesClosedOnUIThread( |
+ int render_process_id, |
+ int render_view_id, |
+ const content::MediaStreamDevices& devices) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ if (!status_icon_) |
+ return; |
+ |
+ DCHECK(!tabs_.empty()); |
+ RemoveCaptureDeviceTab(render_process_id, render_view_id, devices); |
+ |
+ if (tabs_.empty()) |
+ Hide(); |
+} |
+ |
+void MediaStreamCaptureIndicator::CreateStatusTray() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ if (status_icon_) |
+ return; |
+ |
+ // If there is no browser process, we should not create the status tray. |
+ if (!g_browser_process) |
+ return; |
+ |
+ StatusTray* status_tray = g_browser_process->status_tray(); |
+ if (!status_tray) |
+ return; |
+ |
+ status_icon_ = status_tray->CreateStatusIcon(); |
+ |
+ status_icon_->SetToolTip(l10n_util::GetStringFUTF16( |
+ IDS_MEDIA_STREAM_STATUS_TRAY_TOOLTIP, |
+ l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); |
+ |
+ EnsureStatusTrayIcon(); |
+ DCHECK(!icon_image_.empty()); |
+ |
+ status_icon_->SetImage(icon_image_); |
+} |
+ |
+void MediaStreamCaptureIndicator::EnsureStatusTrayIcon() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ if (icon_image_.empty()) { |
+ icon_image_ = *ResourceBundle::GetSharedInstance().GetBitmapNamed( |
+ IDR_MEDIA_STREAM_CAPTURE_LED); |
+ } |
+} |
+ |
+void MediaStreamCaptureIndicator::ShowBalloon( |
+ int render_process_id, |
+ int render_view_id, |
+ const content::MediaStreamDevices& devices) const { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ string16 title = l10n_util::GetStringFUTF16( |
+ IDS_MEDIA_STREAM_STATUS_TRAY_BALLOON_TITLE, |
+ l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); |
+ |
+ int message_id = IDS_MEDIA_STREAM_STATUS_TRAY_BALLOON_BODY_AUDIO_AND_VIDEO; |
+ if (devices.size() == 1) { |
+ if (devices.front().type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE) |
+ message_id = IDS_MEDIA_STREAM_STATUS_TRAY_BALLOON_BODY_AUDIO_ONLY; |
+ else |
+ message_id = IDS_MEDIA_STREAM_STATUS_TRAY_BALLOON_BODY_VIDEO_ONLY; |
+ } |
+ |
+ string16 message = l10n_util::GetStringFUTF16( |
+ message_id, GetTitle(render_process_id, render_view_id)); |
+ |
+ status_icon_->DisplayBalloon(icon_image_, title, message); |
+} |
+ |
+void MediaStreamCaptureIndicator::Hide() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ if (!status_icon_) |
+ return; |
+ |
+ // If there is no browser process, we should not do anything. |
+ if (!g_browser_process) |
+ return; |
+ |
+ StatusTray* status_tray = g_browser_process->status_tray(); |
+ if (status_tray != NULL) { |
+ status_tray->RemoveStatusIcon(status_icon_); |
+ status_icon_ = NULL; |
+ } |
+} |
+ |
+void MediaStreamCaptureIndicator::UpdateStatusTrayIconContextMenu() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ scoped_ptr<ui::SimpleMenuModel> menu(new ui::SimpleMenuModel(this)); |
+ |
+ for (CaptureDeviceTabList::iterator iter = tabs_.begin(); |
+ iter != tabs_.end(); ++iter) { |
+ // Search backward to see if the tab has been added. |
+ CaptureDeviceTabList::iterator iter_backward = std::find_if( |
+ tabs_.begin(), iter, TabEquals(iter->render_process_id, |
+ iter->render_view_id)); |
+ // Do nothing if the tab has been added to the menu. |
+ if (iter_backward != iter) |
+ continue; |
+ |
+ string16 tab_title = GetTitle(iter->render_process_id, |
+ iter->render_view_id); |
+ // The tab has gone away. |
+ if (tab_title.empty()) |
+ continue; |
+ |
+ string16 message = l10n_util::GetStringFUTF16( |
+ IDS_MEDIA_STREAM_STATUS_TRAY_MENU_ITEM, tab_title); |
+ menu->AddItem(IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_FIRST, message); |
+ menu->AddSeparator(); |
+ } |
+ |
+ // The icon will take the ownership of the passed context menu. |
+ status_icon_->SetContextMenu(menu.release()); |
+} |
+ |
+void MediaStreamCaptureIndicator::AddCaptureDeviceTab( |
+ int render_process_id, |
+ int render_view_id, |
+ const content::MediaStreamDevices& devices) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ content::MediaStreamDevices::const_iterator dev = devices.begin(); |
+ for (; dev != devices.end(); ++dev) { |
+ DCHECK(dev->type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE || |
+ dev->type == content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE); |
+ tabs_.push_back(CaptureDeviceTab(render_process_id, |
+ render_view_id, |
+ dev->type)); |
+ } |
+ |
+ UpdateStatusTrayIconContextMenu(); |
+} |
+ |
+void MediaStreamCaptureIndicator::RemoveCaptureDeviceTab( |
+ int render_process_id, |
+ int render_view_id, |
+ const content::MediaStreamDevices& devices) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ content::MediaStreamDevices::const_iterator dev = devices.begin(); |
+ for (; dev != devices.end(); ++dev) { |
+ CaptureDeviceTabList::iterator iter = std::find_if( |
+ tabs_.begin(), tabs_.end(), TabEquals(render_process_id, |
+ render_view_id, |
+ dev->type)); |
+ if (iter != tabs_.end()) { |
+ tabs_.erase(iter); |
+ } else { |
+ DLOG(ERROR) << "Failed to find MediaStream host " |
+ << GetTitle(render_process_id, render_view_id) |
+ << " for device " << dev->name |
+ << " for type " << dev->type; |
+ } |
+ } |
+ |
+ if (!tabs_.empty()) |
+ UpdateStatusTrayIconContextMenu(); |
+} |
+ |
+string16 MediaStreamCaptureIndicator::GetTitle(int render_process_id, |
+ int render_view_id) const { |
+ WebContents* tab_content = tab_util::GetWebContentsByID( |
+ render_process_id, render_view_id); |
+ if (!tab_content) |
+ return NULL; |
+ |
+ string16 tab_title = tab_content->GetTitle(); |
+ if (tab_title.empty()) { |
+ GURL url = tab_content->GetURL(); |
+ tab_title = UTF8ToUTF16(url.spec()); |
+ // Force URL to be LTR. |
+ tab_title = base::i18n::GetDisplayStringInLTRDirectionality(tab_title); |
+ } else { |
+ // Sets the title explicitly as LTR format. Please look at the comments in |
+ // TaskManagerTabContentsResource::GetTitle() for the reasons of doing this. |
+ base::i18n::AdjustStringForLocaleDirection(&tab_title); |
+ } |
+ |
+ return tab_title; |
+} |