Bug 1835804 - Completely block from doing audio decoding on Content and RDD r=alwu

Differential Revision: https://phabricator.services.mozilla.com/D179699
This commit is contained in:
Alexandre Lissy 2023-06-16 15:38:55 +00:00
parent c27a7a2b3e
commit 63186bc601
18 changed files with 316 additions and 112 deletions

View File

@ -36,6 +36,7 @@
#include "mozilla/TaskQueue.h"
#include "mozilla/Unused.h"
#include "nsContentUtils.h"
#include "nsLiteralString.h"
#include "nsPrintfCString.h"
#include "nsTHashSet.h"
@ -3263,6 +3264,9 @@ Maybe<nsCString> MediaFormatReader::GetAudioProcessPerCodec() {
if (!StaticPrefs::media_utility_process_enabled()) {
audioProcessPerCodecName += ",utility-disabled"_ns;
}
if (StaticPrefs::media_allow_audio_non_utility()) {
audioProcessPerCodecName += ",allow-non-utility"_ns;
}
}
return Some(audioProcessPerCodecName);
}

View File

@ -5,7 +5,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "RemoteDecoderManagerChild.h"
#include "ErrorList.h"
#include "PDMFactory.h"
#include "PlatformDecoderModule.h"
#include "RemoteAudioDecoder.h"
#include "RemoteMediaDataDecoder.h"
#include "RemoteVideoDecoder.h"
@ -21,9 +23,11 @@
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/layers/ISurfaceAllocator.h"
#include "mozilla/ipc/UtilityAudioDecoderChild.h"
#include "mozilla/MozPromise.h"
#include "mozilla/StaticPrefs_media.h"
#include "nsContentUtils.h"
#include "nsIObserver.h"
#include "mozilla/StaticPrefs_media.h"
#include "nsPrintfCString.h"
#ifdef MOZ_WMF_MEDIA_ENGINE
# include "MFMediaEngineChild.h"
@ -307,7 +311,17 @@ RemoteDecoderManagerChild::CreateAudioDecoder(
} else if (aLocation == RemoteDecodeIn::UtilityProcess_MFMediaEngineCDM) {
launchPromise = LaunchUtilityProcessIfNeeded(aLocation);
} else {
launchPromise = LaunchRDDProcessIfNeeded();
if (StaticPrefs::media_allow_audio_non_utility()) {
launchPromise = LaunchRDDProcessIfNeeded();
} else {
return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject(
MediaResult(
NS_ERROR_DOM_MEDIA_DENIED_IN_NON_UTILITY,
nsPrintfCString("%s is not allowed to perform audio decoding",
RemoteDecodeInToStr(aLocation))
.get()),
__func__);
}
}
LOG("Create audio decoder in %s", RemoteDecodeInToStr(aLocation));

View File

@ -150,30 +150,37 @@ class PDMInitializer final {
}
static void InitContentPDMs() {
#if !defined(MOZ_WIDGET_ANDROID) // Still required for video?
if (StaticPrefs::media_allow_audio_non_utility()) {
#endif // !defined(MOZ_WIDGET_ANDROID)
#ifdef XP_WIN
if (!IsWin7AndPre2000Compatible()) {
if (!IsWin7AndPre2000Compatible()) {
# ifdef MOZ_WMF
if (!StaticPrefs::media_rdd_process_enabled() ||
!StaticPrefs::media_rdd_wmf_enabled() ||
!StaticPrefs::media_utility_process_enabled() ||
!StaticPrefs::media_utility_wmf_enabled()) {
WMFDecoderModule::Init();
}
if (!StaticPrefs::media_rdd_process_enabled() ||
!StaticPrefs::media_rdd_wmf_enabled() ||
!StaticPrefs::media_utility_process_enabled() ||
!StaticPrefs::media_utility_wmf_enabled()) {
WMFDecoderModule::Init();
}
# endif
}
}
#endif
#ifdef MOZ_APPLEMEDIA
AppleDecoderModule::Init();
AppleDecoderModule::Init();
#endif
#ifdef MOZ_OMX
OmxDecoderModule::Init();
OmxDecoderModule::Init();
#endif
#ifdef MOZ_FFVPX
FFVPXRuntimeLinker::Init();
FFVPXRuntimeLinker::Init();
#endif
#ifdef MOZ_FFMPEG
FFmpegRuntimeLinker::Init();
FFmpegRuntimeLinker::Init();
#endif
#if !defined(MOZ_WIDGET_ANDROID) // Still required for video?
}
#endif // !defined(MOZ_WIDGET_ANDROID)
RemoteDecoderManagerChild::Init();
}
@ -647,41 +654,51 @@ void PDMFactory::CreateContentPDMs() {
}
#endif
#if !defined(MOZ_WIDGET_ANDROID) // Still required for video?
if (StaticPrefs::media_allow_audio_non_utility()) {
#endif // !defined(MOZ_WIDGET_ANDROID)
#ifdef XP_WIN
if (StaticPrefs::media_wmf_enabled() && !IsWin7AndPre2000Compatible()) {
if (StaticPrefs::media_wmf_enabled() && !IsWin7AndPre2000Compatible()) {
# ifdef MOZ_WMF
if (!StaticPrefs::media_rdd_process_enabled() ||
!StaticPrefs::media_rdd_wmf_enabled()) {
if (!CreateAndStartupPDM<WMFDecoderModule>()) {
mFailureFlags += DecoderDoctorDiagnostics::Flags::WMFFailedToLoad;
if (!StaticPrefs::media_rdd_process_enabled() ||
!StaticPrefs::media_rdd_wmf_enabled()) {
if (!CreateAndStartupPDM<WMFDecoderModule>()) {
mFailureFlags += DecoderDoctorDiagnostics::Flags::WMFFailedToLoad;
}
}
}
# endif
} else if (StaticPrefs::media_decoder_doctor_wmf_disabled_is_failure()) {
mFailureFlags += DecoderDoctorDiagnostics::Flags::WMFFailedToLoad;
}
} else if (StaticPrefs::media_decoder_doctor_wmf_disabled_is_failure()) {
mFailureFlags += DecoderDoctorDiagnostics::Flags::WMFFailedToLoad;
}
#endif
#ifdef MOZ_APPLEMEDIA
CreateAndStartupPDM<AppleDecoderModule>();
CreateAndStartupPDM<AppleDecoderModule>();
#endif
#ifdef MOZ_OMX
if (StaticPrefs::media_omx_enabled()) {
CreateAndStartupPDM<OmxDecoderModule>();
}
if (StaticPrefs::media_omx_enabled()) {
CreateAndStartupPDM<OmxDecoderModule>();
}
#endif
#ifdef MOZ_FFVPX
if (StaticPrefs::media_ffvpx_enabled()) {
CreateAndStartupPDM<FFVPXRuntimeLinker>();
}
if (StaticPrefs::media_ffvpx_enabled()) {
CreateAndStartupPDM<FFVPXRuntimeLinker>();
}
#endif
#ifdef MOZ_FFMPEG
if (StaticPrefs::media_ffmpeg_enabled() &&
!CreateAndStartupPDM<FFmpegRuntimeLinker>()) {
mFailureFlags += GetFailureFlagBasedOnFFmpegStatus(
FFmpegRuntimeLinker::LinkStatusCode());
}
if (StaticPrefs::media_ffmpeg_enabled() &&
!CreateAndStartupPDM<FFmpegRuntimeLinker>()) {
mFailureFlags += GetFailureFlagBasedOnFFmpegStatus(
FFmpegRuntimeLinker::LinkStatusCode());
}
#endif
CreateAndStartupPDM<AgnosticDecoderModule>();
#if !defined(MOZ_WIDGET_ANDROID) // Still required for video?
}
#endif // !defined(MOZ_WIDGET_ANDROID)
// Android still needs this, the actual decoder is remoted on java side
#ifdef MOZ_WIDGET_ANDROID
if (StaticPrefs::media_android_media_codec_enabled()) {
StartupPDM(AndroidDecoderModule::Create(),
@ -689,8 +706,6 @@ void PDMFactory::CreateContentPDMs() {
}
#endif
CreateAndStartupPDM<AgnosticDecoderModule>();
if (StaticPrefs::media_gmp_decoder_enabled() &&
!StartupPDM(GMPDecoderModule::Create(),
StaticPrefs::media_gmp_decoder_preferred())) {

View File

@ -52,6 +52,7 @@ support-files =
../../../../dom/media/test/small-shot.mp3
../../../../dom/media/test/small-shot.m4a
../../../../dom/media/test/small-shot.flac
head-multiple.js
[browser_utility_profiler.js]
support-files =
../../../../tools/profiler/tests/shared-head.js

View File

@ -0,0 +1,13 @@
[DEFAULT]
support-files =
head.js
head-multiple.js
prefs =
media.allow-audio-non-utility=true
[browser_utility_multipleAudio_fallback.js]
support-files =
../../../../dom/media/test/small-shot.ogg
../../../../dom/media/test/small-shot.mp3
../../../../dom/media/test/small-shot.m4a
../../../../dom/media/test/small-shot.flac

View File

@ -0,0 +1,14 @@
[DEFAULT]
support-files =
head.js
head-multiple.js
prefs =
media.allow-audio-non-utility=true
media.rdd-process.enabled=false
[browser_utility_multipleAudio_fallback_content.js]
support-files =
../../../../dom/media/test/small-shot.ogg
../../../../dom/media/test/small-shot.mp3
../../../../dom/media/test/small-shot.m4a
../../../../dom/media/test/small-shot.flac

View File

@ -12,15 +12,18 @@ Services.scriptloader.loadSubScript(
add_setup(async function testNoTelemetry() {
await Telemetry.clearScalars();
await SpecialPowers.pushPrefEnv({
set: [["media.allow-audio-non-utility", true]],
});
});
add_task(async function testAudioDecodingInContent() {
await runTest({ expectUtility: false, expectRDD: false });
});
add_task(async function testUtilityTelemetry() {
add_task(async function testContentTelemetry() {
const codecs = ["vorbis", "mp3", "aac", "flac"];
const extraKey = ",rdd-disabled,utility-disabled";
const extraKey = ",rdd-disabled,utility-disabled,allow-non-utility";
await verifyTelemetryForProcess("tab", codecs, extraKey);
const platform = Services.appinfo.OS;

View File

@ -12,6 +12,9 @@ Services.scriptloader.loadSubScript(
add_setup(async function testNoTelemetry() {
await Telemetry.clearScalars();
await SpecialPowers.pushPrefEnv({
set: [["media.allow-audio-non-utility", true]],
});
});
add_task(async function testAudioDecodingInRDD() {
@ -19,7 +22,7 @@ add_task(async function testAudioDecodingInRDD() {
});
add_task(async function testRDDTelemetry() {
const extraKey = ",utility-disabled";
const extraKey = ",utility-disabled,allow-non-utility";
const platform = Services.appinfo.OS;
for (let exp of utilityPerCodecs[platform]) {
await verifyNoTelemetryForProcess(exp.process, exp.codecs, extraKey);

View File

@ -3,71 +3,43 @@
"use strict";
async function runTest(expectUtility) {
info(
`Running tests with decoding from Utility or RDD: expectUtility=${expectUtility}`
/* import-globals-from head-multiple.js */
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/ipc/glue/test/browser/head-multiple.js",
this
);
add_setup(async function checkAudioDecodingNonUtility() {
const isAudioDecodingNonUtilityAllowed = await SpecialPowers.getBoolPref(
"media.allow-audio-non-utility"
);
// Utility should now be the default, so dont toggle the pref unless we test
// RDD
if (!expectUtility) {
await SpecialPowers.pushPrefEnv({
set: [["media.utility-process.enabled", expectUtility]],
});
}
const platform = Services.appinfo.OS;
for (let { src, expectations } of audioTestData()) {
if (!(platform in expectations)) {
info(`Skipping ${src} for ${platform}`);
continue;
}
const expectation = expectations[platform];
info(`Add media tabs: ${src}`);
let tabs = [await addMediaTab(src), await addMediaTab(src)];
let playback = [];
info("Play tabs");
for (let tab of tabs) {
playback.push(
play(
tab,
expectUtility ? expectation.process : "RDD",
expectation.decoder
)
);
}
info("Wait all playback");
await Promise.all(playback);
let allstop = [];
info("Stop tabs");
for (let tab of tabs) {
allstop.push(stop(tab));
}
info("Wait all stop");
await Promise.all(allstop);
let remove = [];
info("Remove tabs");
for (let tab of tabs) {
remove.push(BrowserTestUtils.removeTab(tab));
}
info("Wait all tabs to be removed");
await Promise.all(remove);
}
}
ok(
!isAudioDecodingNonUtilityAllowed,
"Audio decoding should not be allowed in non utility by default"
);
});
add_task(async function testAudioDecodingInUtility() {
await runTest(true);
await runTest({ expectUtility: true });
});
add_task(async function testAudioDecodingInRDD() {
await runTest(false);
add_task(async function testFailureAudioDecodingInRDD() {
await runTest({ expectUtility: false, expectError: true });
});
add_task(async function testFailureAudioDecodingInContent() {
const platform = Services.appinfo.OS;
if (platform === "WINNT") {
ok(
true,
"Manually skippig on Windows because of gfx killing us, cf browser.ini"
);
return;
}
await SpecialPowers.pushPrefEnv({
set: [["media.rdd-process.enabled", false]],
});
await runTest({ expectUtility: false, expectRDD: false, expectError: true });
});

View File

@ -0,0 +1,22 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* import-globals-from head-multiple.js */
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/ipc/glue/test/browser/head-multiple.js",
this
);
add_setup(async function checkAudioDecodingNonUtility() {
const isAudioDecodingNonUtilityAllowed = await SpecialPowers.getBoolPref(
"media.allow-audio-non-utility"
);
ok(isAudioDecodingNonUtilityAllowed, "Audio decoding has been allowed");
});
add_task(async function testFallbackAudioDecodingInRDD() {
await runTest({ expectUtility: false, expectError: false });
});

View File

@ -0,0 +1,22 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* import-globals-from head-multiple.js */
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/ipc/glue/test/browser/head-multiple.js",
this
);
add_setup(async function checkAudioDecodingNonUtility() {
const isAudioDecodingNonUtilityAllowed = await SpecialPowers.getBoolPref(
"media.allow-audio-non-utility"
);
ok(isAudioDecodingNonUtilityAllowed, "Audio decoding has been allowed");
});
add_task(async function testFallbackAudioDecodingInContent() {
await runTest({ expectContent: true });
});

View File

@ -0,0 +1,78 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* import-globals-from head.js */
async function runTest({
expectUtility = false,
expectRDD = false,
expectContent = false,
expectError = false,
}) {
info(`Running tests with decoding from somewhere`);
info(` expectUtility: ${expectUtility}`);
info(` expectRDD: ${expectRDD}`);
info(` expectContent: ${expectContent}`);
// Utility should now be the default, so dont toggle the pref unless we test
// RDD
if (!expectUtility) {
await SpecialPowers.pushPrefEnv({
set: [["media.utility-process.enabled", expectUtility]],
});
}
const platform = Services.appinfo.OS;
for (let { src, expectations } of audioTestData()) {
if (!(platform in expectations)) {
info(`Skipping ${src} for ${platform}`);
continue;
}
const expectation = expectations[platform];
info(`Add media tabs: ${src}`);
let tabs = [await addMediaTab(src), await addMediaTab(src)];
let playback = [];
info("Play tabs");
for (let tab of tabs) {
playback.push(
play(
tab,
expectUtility && !expectContent && !expectError
? expectation.process
: "RDD",
expectation.decoder,
expectContent,
false, // expectJava
expectError
)
);
}
info("Wait all playback");
await Promise.all(playback);
let allstop = [];
info("Stop tabs");
for (let tab of tabs) {
allstop.push(stop(tab));
}
info("Wait all stop");
await Promise.all(allstop);
let remove = [];
info("Remove tabs");
for (let tab of tabs) {
remove.push(BrowserTestUtils.removeTab(tab));
}
info("Wait all tabs to be removed");
await Promise.all(remove);
}
}

View File

@ -88,9 +88,13 @@ async function waitForValue(process, codecNames, extra = "") {
);
}
async function runTest({ expectUtility = false, expectRDD = false }) {
async function runTest({
expectUtility = false,
expectRDD = false,
expectError = false,
}) {
info(
`Running tests with decoding from Utility or RDD: expectUtility=${expectUtility} expectRDD=${expectRDD}`
`Running tests with decoding from Utility or RDD: expectUtility=${expectUtility} expectRDD=${expectRDD} expectError=${expectError}`
);
await SpecialPowers.pushPrefEnv({
@ -119,7 +123,9 @@ async function runTest({ expectUtility = false, expectRDD = false }) {
tab,
expectUtility ? expectation.process : "RDD",
expectation.decoder,
!expectUtility && !expectRDD
!expectUtility && !expectRDD,
false,
expectError
);
info("Stop tab");

View File

@ -206,12 +206,13 @@ async function play(
expectUtility,
expectDecoder,
expectContent = false,
expectJava = false
expectJava = false,
expectError = false
) {
let browser = tab.linkedBrowser;
return SpecialPowers.spawn(
browser,
[expectUtility, expectDecoder, expectContent, expectJava],
[expectUtility, expectDecoder, expectContent, expectJava, expectError],
checkAudioDecoder
);
}
@ -238,7 +239,8 @@ async function checkAudioDecoder(
expectedProcess,
expectedDecoder,
expectContent = false,
expectJava = false
expectJava = false,
expectError = false
) {
const doc = typeof content !== "undefined" ? content.document : document;
let audio = doc.querySelector("audio");
@ -258,7 +260,7 @@ async function checkAudioDecoder(
audioDecoderName.indexOf(`(${expectedProcess} remote)`) > 0;
const isJavaRemote = audioDecoderName.indexOf("(remote)") > 0;
const isOk =
(isExpectedProcess && !isJavaRemote && !expectContent && !expectJava) || // Running in Utility/RDD
(isExpectedProcess && !isJavaRemote && !expectContent && !expectJava) || // Running in Utility
(expectJava && !isExpectedProcess && isJavaRemote) || // Running in Java remote
(expectContent && !isExpectedProcess && !isJavaRemote); // Running in Content
@ -286,6 +288,24 @@ async function checkAudioDecoder(
audio.addEventListener("timeupdate", timeUpdateHandler, { once: true });
};
audio.addEventListener("error", async err => {
info(
`Received HTML media error: ${audio.error.code}: ${audio.error.message}`
);
if (expectError) {
const w = typeof content !== "undefined" ? content.window : window;
ok(
audio.error.code === w.MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED ||
w.MediaError.MEDIA_ERR_DECODE,
"Media supported but decoding failed"
);
resolve();
} else {
info(`Unexpected error`);
reject();
}
});
audio.addEventListener("canplaythrough", startPlaybackHandler, {
once: true,
});
@ -304,6 +324,7 @@ async function runMochitestUtilityAudio(
expectDecoder,
expectContent = false,
expectJava = false,
expectError = false,
} = {}
) {
info(`Add media: ${src}`);
@ -316,7 +337,8 @@ async function runMochitestUtilityAudio(
expectUtility,
expectDecoder,
expectContent,
expectJava
expectJava,
expectError
);
info(`Pause media: ${src}`);

View File

@ -6,6 +6,8 @@
BROWSER_CHROME_MANIFESTS += [
"browser.ini",
"browser_audio_fallback.ini",
"browser_audio_fallback_content.ini",
"browser_audio_shutdown.ini",
"browser_child_hang.ini",
]

View File

@ -20,7 +20,7 @@ SimpleTest.waitForExplicitFinish();
}
try {
await runMochitestUtilityAudio(src, { expectUtility: "", expectDecoder: expectations[platform].decoder, expectContent: true, expectJava: false });
await runMochitestUtilityAudio(src, { expectUtility: "", expectDecoder: expectations[platform].decoder, expectContent: true, expectJava: false, expectError: true });
} catch (ex) {
ok(false, "Failure");
}
@ -30,7 +30,7 @@ SimpleTest.waitForExplicitFinish();
"small-shot.m4a",
]) {
try {
await runMochitestUtilityAudio(src, { expectUtility: "", expectDecoder: "android decoder", expectContent: false, expectJava: true });
await runMochitestUtilityAudio(src, { expectUtility: "", expectDecoder: "android decoder", expectContent: false, expectJava: true, expectError: false });
} catch (ex) {
ok(false, `Failure ${ex}`);
}

View File

@ -10091,6 +10091,18 @@
mirror: always
#endif # ANDROID
# Now we will completely disable the ability to perform audio decoding outside
# of Utility.
# We do that only on nightly builds for now.
- name: media.allow-audio-non-utility
type: RelaxedAtomicBool
#if defined(NIGHTLY_BUILD)
value: false
#else
value: true
#endif // defined(NIGHTLY_BUILD)
mirror: always
#ifdef MOZ_OMX
- name: media.omx.enabled
type: bool

View File

@ -1175,6 +1175,7 @@ with modules["DOM_MEDIA"]:
errors["NS_ERROR_DOM_MEDIA_CUBEB_INITIALIZATION_ERR"] = FAILURE(101)
errors["NS_ERROR_DOM_MEDIA_EXTERNAL_ENGINE_NOT_SUPPORTED_ERR"] = FAILURE(102)
errors["NS_ERROR_DOM_MEDIA_CDM_PROXY_NOT_SUPPORTED_ERR"] = FAILURE(103)
errors["NS_ERROR_DOM_MEDIA_DENIED_IN_NON_UTILITY"] = FAILURE(104)
# =======================================================================
# 42: NS_ERROR_MODULE_URL_CLASSIFIER