Bug 1908056 - Add media.devices.enumerate.legacy.allowlist exception for slack. r=dbaker

Differential Revision: https://phabricator.services.mozilla.com/D224367
This commit is contained in:
Jan-Ivar Bruaroey 2024-10-04 00:03:38 +00:00
parent 4921bad37a
commit 0ea5919a4d
7 changed files with 101 additions and 69 deletions

View File

@ -231,6 +231,21 @@ void MediaDevices::MaybeResumeDeviceExposure() {
mHaveUnprocessedDeviceListChange = false;
}
static bool IsLegacyMode(nsPIDOMWindowInner* window) {
if (StaticPrefs::media_devices_enumerate_legacy_enabled()) {
return true;
}
if (window->GetDocumentURI()) {
nsAutoCString host;
window->GetDocumentURI()->GetAsciiHost(host);
if (media::HostnameInPref("media.devices.enumerate.legacy.allowlist",
host)) {
return true;
}
}
return false;
}
RefPtr<MediaDeviceSetRefCnt> MediaDevices::FilterExposedDevices(
const MediaDeviceSet& aDevices) const {
nsPIDOMWindowInner* window = GetOwnerWindow();
@ -250,7 +265,7 @@ RefPtr<MediaDeviceSetRefCnt> MediaDevices::FilterExposedDevices(
!Preferences::GetBool("media.setsinkid.enabled") ||
!FeaturePolicyUtils::IsFeatureAllowed(doc, u"speaker-selection"_ns);
bool legacy = StaticPrefs::media_devices_enumerate_legacy_enabled();
bool legacy = IsLegacyMode(window);
bool outputIsDefault = true; // First output is the default.
bool haveDefaultOutput = false;
nsTHashSet<nsString> exposedMicrophoneGroupIds;
@ -467,7 +482,7 @@ void MediaDevices::ResolveEnumerateDevicesPromise(
nsCOMPtr<nsPIDOMWindowInner> window = GetOwnerWindow();
auto windowId = window->WindowID();
nsTArray<RefPtr<MediaDeviceInfo>> infos;
bool legacy = StaticPrefs::media_devices_enumerate_legacy_enabled();
bool legacy = IsLegacyMode(window);
bool capturePermitted =
legacy &&
MediaManager::Get()->IsActivelyCapturingOrHasAPermission(windowId);

View File

@ -7,10 +7,64 @@
#include "MediaUtils.h"
#include "mozilla/AppShutdown.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "nsNetUtil.h"
namespace mozilla::media {
bool HostnameInPref(const char* aPref, const nsCString& aHostName) {
auto HostInDomain = [](const nsCString& aHost, const nsCString& aPattern) {
int32_t patternOffset = 0;
int32_t hostOffset = 0;
// Act on '*.' wildcard in the left-most position in a domain pattern.
if (StringBeginsWith(aPattern, nsCString("*."))) {
patternOffset = 2;
// Ignore the lowest level sub-domain for the hostname.
hostOffset = aHost.FindChar('.') + 1;
if (hostOffset <= 1) {
// Reject a match between a wildcard and a TLD or '.foo' form.
return false;
}
}
nsDependentCString hostRoot(aHost, hostOffset);
return hostRoot.EqualsIgnoreCase(aPattern.BeginReading() + patternOffset);
};
nsCString domainList;
nsresult rv = Preferences::GetCString(aPref, domainList);
if (NS_FAILED(rv)) {
return false;
}
domainList.StripWhitespace();
if (domainList.IsEmpty() || aHostName.IsEmpty()) {
return false;
}
// Test each domain name in the comma separated list
// after converting from UTF8 to ASCII. Each domain
// must match exactly or have a single leading '*.' wildcard.
for (const nsACString& each : domainList.Split(',')) {
nsCString domainPattern;
rv = NS_DomainToASCIIAllowAnyGlyphfulASCII(each, domainPattern);
if (NS_SUCCEEDED(rv)) {
if (HostInDomain(aHostName, domainPattern)) {
return true;
}
} else {
NS_WARNING("Failed to convert UTF-8 host to ASCII");
}
}
return false;
}
nsCOMPtr<nsIAsyncShutdownClient> GetShutdownBarrier() {
nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdownService();
if (!svc) {

View File

@ -28,6 +28,12 @@ class nsIEventTarget;
namespace mozilla::media {
/* Utility function, given a string pref and an URI, returns whether or not
* the URI occurs in the pref. Wildcards are supported (e.g. *.example.com)
* and multiple hostnames can be present, separated by commas.
*/
bool HostnameInPref(const char* aPrefList, const nsCString& aHostName);
/* media::NewRunnableFrom() - Create a Runnable from a lambda.
*
* Passing variables (closures) to an async function is clunky with Runnable:

View File

@ -53,6 +53,7 @@
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Sprintf.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/media/MediaUtils.h"
#ifdef XP_WIN
// We need to undef the MS macro for Document::CreateEvent
@ -396,9 +397,9 @@ PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal)
mEffectiveTLDPlus1);
}
mRtxIsAllowed = !HostnameInPref(
mRtxIsAllowed = !media::HostnameInPref(
"media.peerconnection.video.use_rtx.blocklist", mHostname);
mDuplicateFingerprintQuirk = HostnameInPref(
mDuplicateFingerprintQuirk = media::HostnameInPref(
"media.peerconnection.sdp.quirk.duplicate_fingerprint.allowlist",
mHostname);
}
@ -2160,60 +2161,6 @@ void PeerConnectionImpl::DumpPacket_m(size_t level, dom::mozPacketDumpType type,
mPCObserver->OnPacket(level, type, sending, arrayBuffer, jrv);
}
bool PeerConnectionImpl::HostnameInPref(const char* aPref,
const nsCString& aHostName) {
auto HostInDomain = [](const nsCString& aHost, const nsCString& aPattern) {
int32_t patternOffset = 0;
int32_t hostOffset = 0;
// Act on '*.' wildcard in the left-most position in a domain pattern.
if (StringBeginsWith(aPattern, nsCString("*."))) {
patternOffset = 2;
// Ignore the lowest level sub-domain for the hostname.
hostOffset = aHost.FindChar('.') + 1;
if (hostOffset <= 1) {
// Reject a match between a wildcard and a TLD or '.foo' form.
return false;
}
}
nsDependentCString hostRoot(aHost, hostOffset);
return hostRoot.EqualsIgnoreCase(aPattern.BeginReading() + patternOffset);
};
nsCString domainList;
nsresult rv = Preferences::GetCString(aPref, domainList);
if (NS_FAILED(rv)) {
return false;
}
domainList.StripWhitespace();
if (domainList.IsEmpty() || aHostName.IsEmpty()) {
return false;
}
// Test each domain name in the comma separated list
// after converting from UTF8 to ASCII. Each domain
// must match exactly or have a single leading '*.' wildcard.
for (const nsACString& each : domainList.Split(',')) {
nsCString domainPattern;
rv = NS_DomainToASCIIAllowAnyGlyphfulASCII(each, domainPattern);
if (NS_SUCCEEDED(rv)) {
if (HostInDomain(aHostName, domainPattern)) {
return true;
}
} else {
NS_WARNING("Failed to convert UTF-8 host to ASCII");
}
}
return false;
}
nsresult PeerConnectionImpl::EnablePacketDump(unsigned long level,
dom::mozPacketDumpType type,
bool sending) {
@ -4452,7 +4399,7 @@ bool PeerConnectionImpl::GetPrefObfuscateHostAddresses() const {
"media.peerconnection.ice.obfuscate_host_addresses", false);
obfuscate_host_addresses &=
!MediaManager::Get()->IsActivelyCapturingOrHasAPermission(winId);
obfuscate_host_addresses &= !PeerConnectionImpl::HostnameInPref(
obfuscate_host_addresses &= !media::HostnameInPref(
"media.peerconnection.ice.obfuscate_host_addresses.blocklist", mHostname);
obfuscate_host_addresses &= XRE_IsContentProcess();

View File

@ -534,11 +534,6 @@ class PeerConnectionImpl final
return mTimestampMaker;
}
// Utility function, given a string pref and an URI, returns whether or not
// the URI occurs in the pref. Wildcards are supported (e.g. *.example.com)
// and multiple hostnames can be present, separated by commas.
static bool HostnameInPref(const char* aPrefList, const nsCString& aHostName);
void StampTimecard(const char* aEvent);
bool RelayOnly() const {

View File

@ -47,12 +47,9 @@ function validateDevice({kind, label, deviceId, groupId}) {
isnot(groupId, "", "groupId must be present.");
}
runTest(async () => {
await pushPrefs(["media.navigator.streams.fake", true],
["media.devices.enumerate.legacy.enabled", true]);
// Validate enumerated devices before gUM (legacy).
// Validate enumerated devices before gUM (legacy).
async function testLegacyEnumerateDevices() {
let devices = await navigator.mediaDevices.enumerateDevices();
ok(devices.length, "At least one device found");
const jsoned = JSON.parse(JSON.stringify(devices));
@ -140,7 +137,17 @@ runTest(async () => {
["media.video_loopback_dev", "none"]);
devices = await navigator.mediaDevices.enumerateDevices();
is(devices.length, 0, "No devices");
}
runTest(async () => {
await withPrefs([["media.navigator.streams.fake", true],
["media.devices.enumerate.legacy.allowlist", "example.com,*.example.com"]],
testLegacyEnumerateDevices);
await withPrefs([["media.navigator.streams.fake", true],
["media.devices.enumerate.legacy.enabled", true]],
testLegacyEnumerateDevices);
});
</script>
</pre>
</body>

View File

@ -11491,6 +11491,14 @@
value: false
mirror: always
# Turns on legacy (non-spec) exposure of camera and microphone information
# from enumerateDevices and devicechange ahead of successful getUserMedia
# calls only for certain domains (ignored if above pref is true).
- name: media.devices.enumerate.legacy.allowlist
type: String
value: "slack.com,*.slack.com"
mirror: never
# WebRTC prefs follow
# Enables auto refresh of peerconnection stats by default