blob: 4005c1d5549a9f7d76e9b400a3c8cec73ee3b757 [file] [log] [blame]
// 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/ui/tabs/tab_utils.h"
#include <utility>
#include "base/feature_list.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
#include "chrome/browser/media/webrtc/media_stream_capture_indicator.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/recently_audible_helper.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/usb/usb_tab_helper.h"
#include "chrome/browser/vr/vr_tab_helper.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "content/public/common/content_features.h"
#include "content/public/common/url_constants.h"
namespace chrome {
TabAlertState GetTabAlertStateForContents(content::WebContents* contents) {
if (!contents)
return TabAlertState::NONE;
scoped_refptr<MediaStreamCaptureIndicator> indicator =
MediaCaptureDevicesDispatcher::GetInstance()->
GetMediaStreamCaptureIndicator();
if (indicator.get()) {
// Currently we only show the icon and tooltip of the highest-priority
// alert on a tab.
// TODO(crbug.com/861961): To show the icon of the highest-priority alert
// with tooltip that notes all the states in play.
if (indicator->IsCapturingDesktop(contents))
return TabAlertState::DESKTOP_CAPTURING;
if (indicator->IsBeingMirrored(contents))
return TabAlertState::TAB_CAPTURING;
if (indicator->IsCapturingUserMedia(contents))
return TabAlertState::MEDIA_RECORDING;
}
if (contents->IsConnectedToBluetoothDevice())
return TabAlertState::BLUETOOTH_CONNECTED;
UsbTabHelper* usb_tab_helper = UsbTabHelper::FromWebContents(contents);
if (usb_tab_helper && usb_tab_helper->IsDeviceConnected())
return TabAlertState::USB_CONNECTED;
// Check if VR content is being presented in a headset.
// NOTE: This icon must take priority over the audio alert ones
// because most VR content has audio and its usage is implied by the VR icon.
if (vr::VrTabHelper::IsContentDisplayedInHeadset(contents))
return TabAlertState::VR_PRESENTING_IN_HEADSET;
if (contents->HasPictureInPictureVideo())
return TabAlertState::PIP_PLAYING;
// Only tabs have a RecentlyAudibleHelper, but this function is abused for
// some non-tab WebContents. In that case fall back to using the realtime
// notion of audibility.
bool audible = contents->IsCurrentlyAudible();
auto* audible_helper = RecentlyAudibleHelper::FromWebContents(contents);
if (audible_helper)
audible = audible_helper->WasRecentlyAudible();
if (audible) {
if (contents->IsAudioMuted())
return TabAlertState::AUDIO_MUTING;
return TabAlertState::AUDIO_PLAYING;
}
return TabAlertState::NONE;
}
bool CanToggleAudioMute(content::WebContents* contents) {
switch (chrome::GetTabAlertStateForContents(contents)) {
case TabAlertState::NONE:
case TabAlertState::AUDIO_PLAYING:
case TabAlertState::AUDIO_MUTING:
case TabAlertState::PIP_PLAYING:
case TabAlertState::VR_PRESENTING_IN_HEADSET:
return true;
case TabAlertState::MEDIA_RECORDING:
case TabAlertState::TAB_CAPTURING:
case TabAlertState::BLUETOOTH_CONNECTED:
case TabAlertState::USB_CONNECTED:
case TabAlertState::DESKTOP_CAPTURING:
// The new Audio Service implements muting separately from the tab audio
// capture infrastructure; so the mute state can be toggled independently
// at all times.
//
// TODO(crbug.com/672469): Remove this method once the Audio Service is
// launched.
return base::FeatureList::IsEnabled(features::kAudioServiceAudioStreams);
}
NOTREACHED();
return false;
}
TabMutedReason GetTabAudioMutedReason(content::WebContents* contents) {
LastMuteMetadata::CreateForWebContents(contents); // Ensures metadata exists.
LastMuteMetadata* const metadata =
LastMuteMetadata::FromWebContents(contents);
if (GetTabAlertStateForContents(contents) == TabAlertState::TAB_CAPTURING &&
!base::FeatureList::IsEnabled(features::kAudioServiceAudioStreams)) {
// The legacy tab audio capture implementation in libcontent forces muting
// off because it requires using the same infrastructure.
//
// TODO(crbug.com/672469): Remove this once the Audio Service is launched.
// See comments in CanToggleAudioMute().
metadata->reason = TabMutedReason::MEDIA_CAPTURE;
metadata->extension_id.clear();
}
return metadata->reason;
}
bool SetTabAudioMuted(content::WebContents* contents,
bool mute,
TabMutedReason reason,
const std::string& extension_id) {
DCHECK(contents);
DCHECK(TabMutedReason::NONE != reason);
if (!chrome::CanToggleAudioMute(contents))
return false;
contents->SetAudioMuted(mute);
LastMuteMetadata::CreateForWebContents(contents); // Ensures metadata exists.
LastMuteMetadata* const metadata =
LastMuteMetadata::FromWebContents(contents);
metadata->reason = reason;
if (reason == TabMutedReason::EXTENSION) {
DCHECK(!extension_id.empty());
metadata->extension_id = extension_id;
} else {
metadata->extension_id.clear();
}
return true;
}
bool AreAllTabsMuted(const TabStripModel& tab_strip,
const std::vector<int>& indices) {
for (auto i = indices.begin(); i != indices.end(); ++i) {
if (!tab_strip.GetWebContentsAt(*i)->IsAudioMuted())
return false;
}
return true;
}
bool IsSiteMuted(const TabStripModel& tab_strip, const int index) {
content::WebContents* web_contents = tab_strip.GetWebContentsAt(index);
// Prevent crashes with null WebContents (https://crbug.com/797647).
if (!web_contents)
return false;
GURL url = web_contents->GetLastCommittedURL();
// chrome:// URLs don't have content settings but can be muted, so just check
// the current muted state and TabMutedReason of the WebContents.
if (url.SchemeIs(content::kChromeUIScheme)) {
return web_contents->IsAudioMuted() &&
GetTabAudioMutedReason(web_contents) ==
TabMutedReason::CONTENT_SETTING_CHROME;
}
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
HostContentSettingsMap* settings =
HostContentSettingsMapFactory::GetForProfile(profile);
return settings->GetContentSetting(url, url, CONTENT_SETTINGS_TYPE_SOUND,
std::string()) == CONTENT_SETTING_BLOCK;
}
bool AreAllSitesMuted(const TabStripModel& tab_strip,
const std::vector<int>& indices) {
for (int tab_index : indices) {
if (!IsSiteMuted(tab_strip, tab_index))
return false;
}
return true;
}
} // namespace chrome
WEB_CONTENTS_USER_DATA_KEY_IMPL(LastMuteMetadata)