fune/toolkit/modules/GMPUtils.sys.mjs
Andrew Osmond babdd651fe Bug 1862051 - Add basic plumbing for downloading Widevine L1 plugins. r=media-playback-reviewers,webidl,saschanaz,alwu
This patch adds support for downloading the Widevine L1 plugin for
Windows and WMF. It is disabled by default. It does not integrate
utilizing the plugin with our WMF framework.

New prefs for the new plugin type include:
- media.gmp-widevinecdm-l1.visible
- media.gmp-widevinecdm-l1.enabled
- media.gmp-widevinecdm-l1.forceInstall

All of the above should be flipped to true in order to make Firefox
install the plugin.

The GMPParent for the plugin can be discovered using SelectPluginForAPI.
The API tags are com.widevine.alpha.experiment and
com.widevine.alpha.experiment2.

Differential Revision: https://phabricator.services.mozilla.com/D192367
2023-11-04 01:32:34 +00:00

263 lines
8.9 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs",
});
// GMP IDs
export const OPEN_H264_ID = "gmp-gmpopenh264";
export const WIDEVINE_L1_ID = "gmp-widevinecdm-l1";
export const WIDEVINE_L3_ID = "gmp-widevinecdm";
export const GMP_PLUGIN_IDS = [OPEN_H264_ID, WIDEVINE_L1_ID, WIDEVINE_L3_ID];
export var GMPUtils = {
/**
* Checks whether or not a given plugin is hidden. Hidden plugins are neither
* downloaded nor displayed in the addons manager.
* @param aPlugin
* The plugin to check.
*/
isPluginHidden(aPlugin) {
if (!this._isPluginSupported(aPlugin) || !this._isPluginVisible(aPlugin)) {
return true;
}
if (!aPlugin.isEME) {
return false;
}
if (!GMPPrefs.getBool(GMPPrefs.KEY_EME_ENABLED, true)) {
return true;
}
return false;
},
/**
* Checks whether or not a given plugin is supported by the current OS.
* @param aPlugin
* The plugin to check.
*/
_isPluginSupported(aPlugin) {
if (this._isPluginForceSupported(aPlugin)) {
return true;
}
if (aPlugin.id == WIDEVINE_L1_ID) {
// The Widevine L1 plugin is currently only available for Windows x64.
return (
AppConstants.MOZ_WMF_CDM &&
AppConstants.platform == "win" &&
lazy.UpdateUtils.ABI.match(/x64/)
);
}
if (aPlugin.id == WIDEVINE_L1_ID || aPlugin.id == WIDEVINE_L3_ID) {
// The Widevine plugin is available for Windows versions Vista and later,
// Mac OSX, and Linux.
return (
AppConstants.platform == "win" ||
AppConstants.platform == "macosx" ||
AppConstants.platform == "linux"
);
}
return true;
},
/**
* Checks whether or not a given plugin is visible in the addons manager
* UI and the "enable DRM" notification box. This can be used to test
* plugins that aren't yet turned on in the mozconfig.
* @param aPlugin
* The plugin to check.
*/
_isPluginVisible(aPlugin) {
return GMPPrefs.getBool(GMPPrefs.KEY_PLUGIN_VISIBLE, false, aPlugin.id);
},
/**
* Checks whether or not a given plugin is forced-supported. This is used
* in automated tests to override the checks that prevent GMPs running on an
* unsupported platform.
* @param aPlugin
* The plugin to check.
*/
_isPluginForceSupported(aPlugin) {
return GMPPrefs.getBool(
GMPPrefs.KEY_PLUGIN_FORCE_SUPPORTED,
false,
aPlugin.id
);
},
_isWindowsOnARM64() {
return (
AppConstants.platform == "win" && lazy.UpdateUtils.ABI.match(/aarch64/)
);
},
_expectedABI(aPlugin) {
let defaultABI = lazy.UpdateUtils.ABI;
let expectedABIs = [defaultABI];
if (aPlugin.id == WIDEVINE_L3_ID && this._isWindowsOnARM64()) {
// On Windows on aarch64, we may use either the x86 or the ARM64 plugin
// as we are still shipping the former to release.
expectedABIs.push(defaultABI.replace(/aarch64/g, "x86"));
}
return expectedABIs.join(",");
},
};
/**
* Manages preferences for GMP addons
*/
export var GMPPrefs = {
KEY_EME_ENABLED: "media.eme.enabled",
KEY_PLUGIN_ENABLED: "media.{0}.enabled",
KEY_PLUGIN_LAST_DOWNLOAD: "media.{0}.lastDownload",
KEY_PLUGIN_LAST_DOWNLOAD_FAILED: "media.{0}.lastDownloadFailed",
KEY_PLUGIN_LAST_DOWNLOAD_FAIL_REASON: "media.{0}.lastDownloadFailReason",
KEY_PLUGIN_LAST_INSTALL_FAILED: "media.{0}.lastInstallFailed",
KEY_PLUGIN_LAST_INSTALL_FAIL_REASON: "media.{0}.lastInstallFailReason",
KEY_PLUGIN_LAST_INSTALL_START: "media.{0}.lastInstallStart",
KEY_PLUGIN_LAST_UPDATE: "media.{0}.lastUpdate",
KEY_PLUGIN_HASHVALUE: "media.{0}.hashValue",
KEY_PLUGIN_VERSION: "media.{0}.version",
KEY_PLUGIN_AUTOUPDATE: "media.{0}.autoupdate",
KEY_PLUGIN_VISIBLE: "media.{0}.visible",
KEY_PLUGIN_ABI: "media.{0}.abi",
KEY_PLUGIN_FORCE_SUPPORTED: "media.{0}.forceSupported",
KEY_PLUGIN_FORCE_INSTALL: "media.{0}.forceInstall",
KEY_PLUGIN_ALLOW_X64_ON_ARM64: "media.{0}.allow-x64-plugin-on-arm64",
KEY_URL: "media.gmp-manager.url",
KEY_URL_OVERRIDE: "media.gmp-manager.url.override",
KEY_CERT_CHECKATTRS: "media.gmp-manager.cert.checkAttributes",
KEY_CERT_REQUIREBUILTIN: "media.gmp-manager.cert.requireBuiltIn",
KEY_CHECK_CONTENT_SIGNATURE: "media.gmp-manager.checkContentSignature",
KEY_UPDATE_LAST_CHECK: "media.gmp-manager.lastCheck",
KEY_UPDATE_LAST_EMPTY_CHECK: "media.gmp-manager.lastEmptyCheck",
KEY_SECONDS_BETWEEN_CHECKS: "media.gmp-manager.secondsBetweenChecks",
KEY_UPDATE_ENABLED: "media.gmp-manager.updateEnabled",
KEY_APP_DISTRIBUTION: "distribution.id",
KEY_APP_DISTRIBUTION_VERSION: "distribution.version",
KEY_BUILDID: "media.gmp-manager.buildID",
KEY_CERTS_BRANCH: "media.gmp-manager.certs.",
KEY_PROVIDER_ENABLED: "media.gmp-provider.enabled",
KEY_LOG_BASE: "media.gmp.log.",
KEY_LOGGING_LEVEL: "media.gmp.log.level",
KEY_LOGGING_DUMP: "media.gmp.log.dump",
/**
* Obtains the specified string preference in relation to the specified plugin.
* @param aKey The preference key value to use.
* @param aDefaultValue The default value if no preference exists.
* @param aPlugin The plugin to scope the preference to.
* @return The obtained preference value, or the defaultValue if none exists.
*/
getString(aKey, aDefaultValue, aPlugin) {
if (
aKey === this.KEY_APP_DISTRIBUTION ||
aKey === this.KEY_APP_DISTRIBUTION_VERSION
) {
return Services.prefs.getDefaultBranch(null).getCharPref(aKey, "default");
}
return Services.prefs.getStringPref(
this.getPrefKey(aKey, aPlugin),
aDefaultValue
);
},
/**
* Obtains the specified int preference in relation to the specified plugin.
* @param aKey The preference key value to use.
* @param aDefaultValue The default value if no preference exists.
* @param aPlugin The plugin to scope the preference to.
* @return The obtained preference value, or the defaultValue if none exists.
*/
getInt(aKey, aDefaultValue, aPlugin) {
return Services.prefs.getIntPref(
this.getPrefKey(aKey, aPlugin),
aDefaultValue
);
},
/**
* Obtains the specified bool preference in relation to the specified plugin.
* @param aKey The preference key value to use.
* @param aDefaultValue The default value if no preference exists.
* @param aPlugin The plugin to scope the preference to.
* @return The obtained preference value, or the defaultValue if none exists.
*/
getBool(aKey, aDefaultValue, aPlugin) {
return Services.prefs.getBoolPref(
this.getPrefKey(aKey, aPlugin),
aDefaultValue
);
},
/**
* Sets the specified string preference in relation to the specified plugin.
* @param aKey The preference key value to use.
* @param aVal The value to set.
* @param aPlugin The plugin to scope the preference to.
*/
setString(aKey, aVal, aPlugin) {
Services.prefs.setStringPref(this.getPrefKey(aKey, aPlugin), aVal);
},
/**
* Sets the specified bool preference in relation to the specified plugin.
* @param aKey The preference key value to use.
* @param aVal The value to set.
* @param aPlugin The plugin to scope the preference to.
*/
setBool(aKey, aVal, aPlugin) {
Services.prefs.setBoolPref(this.getPrefKey(aKey, aPlugin), aVal);
},
/**
* Sets the specified int preference in relation to the specified plugin.
* @param aKey The preference key value to use.
* @param aVal The value to set.
* @param aPlugin The plugin to scope the preference to.
*/
setInt(aKey, aVal, aPlugin) {
Services.prefs.setIntPref(this.getPrefKey(aKey, aPlugin), aVal);
},
/**
* Checks whether or not the specified preference is set in relation to the
* specified plugin.
* @param aKey The preference key value to use.
* @param aPlugin The plugin to scope the preference to.
* @return true if the preference is set, false otherwise.
*/
isSet(aKey, aPlugin) {
return Services.prefs.prefHasUserValue(this.getPrefKey(aKey, aPlugin));
},
/**
* Resets the specified preference in relation to the specified plugin to its
* default.
* @param aKey The preference key value to use.
* @param aPlugin The plugin to scope the preference to.
*/
reset(aKey, aPlugin) {
Services.prefs.clearUserPref(this.getPrefKey(aKey, aPlugin));
},
/**
* Scopes the specified preference key to the specified plugin.
* @param aKey The preference key value to use.
* @param aPlugin The plugin to scope the preference to.
* @return A preference key scoped to the specified plugin.
*/
getPrefKey(aKey, aPlugin) {
return aKey.replace("{0}", aPlugin || "");
},
};