Source code

Revision control

Copy as Markdown

Other Tools

/* 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 { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
});
import {
GMPPrefs,
GMPUtils,
OPEN_H264_ID,
WIDEVINE_L1_ID,
WIDEVINE_L3_ID,
const SEC_IN_A_DAY = 24 * 60 * 60;
// How long to wait after a user enabled EME before attempting to download CDMs.
const GMP_CHECK_DELAY = 10 * 1000; // milliseconds
const XHTML = "http://www.w3.org/1999/xhtml";
const NS_GRE_DIR = "GreD";
const CLEARKEY_PLUGIN_ID = "gmp-clearkey";
const CLEARKEY_VERSION = "0.1";
const FIRST_CONTENT_PROCESS_TOPIC = "ipc:first-content-process-created";
const GMP_LICENSE_INFO = "plugins-gmp-license-info";
const GMP_PRIVACY_INFO = "plugins-gmp-privacy-info";
const GMP_LEARN_MORE = "learn_more_label";
const GMP_PLUGINS = [
{
id: OPEN_H264_ID,
name: "plugins-openh264-name",
description: "plugins-openh264-description",
level: "",
libName: "gmpopenh264",
// The following licenseURL is part of an awful hack to include the OpenH264
// license without having bug 624602 fixed yet, and intentionally ignores
// localisation.
homepageURL: "https://www.openh264.org/",
},
{
id: WIDEVINE_L1_ID,
name: "plugins-widevine-name",
description: "plugins-widevine-description",
level: "L1",
libName: "Google.Widevine.CDM",
homepageURL: "https://www.widevine.com/",
isEME: true,
},
{
id: WIDEVINE_L3_ID,
name: "plugins-widevine-name",
description: "plugins-widevine-description",
level: "L3",
libName: "widevinecdm",
homepageURL: "https://www.widevine.com/",
isEME: true,
},
];
ChromeUtils.defineLazyGetter(
lazy,
"addonsBundle",
() => new Localization(["toolkit/about/aboutAddons.ftl"], true)
);
ChromeUtils.defineLazyGetter(lazy, "gmpService", () =>
Cc["@mozilla.org/gecko-media-plugin-service;1"].getService(
Ci.mozIGeckoMediaPluginChromeService
)
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"gmpProviderEnabled",
GMPPrefs.KEY_PROVIDER_ENABLED
);
var gLogger;
var gLogAppenderDump = null;
function configureLogging() {
if (!gLogger) {
gLogger = lazy.Log.repository.getLogger("Toolkit.GMP");
gLogger.addAppender(
new lazy.Log.ConsoleAppender(new lazy.Log.BasicFormatter())
);
}
gLogger.level = GMPPrefs.getInt(
GMPPrefs.KEY_LOGGING_LEVEL,
lazy.Log.Level.Warn
);
let logDumping = GMPPrefs.getBool(GMPPrefs.KEY_LOGGING_DUMP, false);
if (logDumping != !!gLogAppenderDump) {
if (logDumping) {
gLogAppenderDump = new lazy.Log.DumpAppender(
new lazy.Log.BasicFormatter()
);
gLogger.addAppender(gLogAppenderDump);
} else {
gLogger.removeAppender(gLogAppenderDump);
gLogAppenderDump = null;
}
}
}
/**
* The GMPWrapper provides the info for the various GMP plugins to public
* callers through the API.
*/
function GMPWrapper(aPluginInfo, aRawPluginInfo) {
this._plugin = aPluginInfo;
this._rawPlugin = aRawPluginInfo;
this._log = lazy.Log.repository.getLoggerWithMessagePrefix(
"Toolkit.GMP",
"GMPWrapper(" + this._plugin.id + ") "
);
Services.prefs.addObserver(
GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_ENABLED, this._plugin.id),
this,
true
);
Services.prefs.addObserver(
GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_VERSION, this._plugin.id),
this,
true
);
if (this._plugin.isEME) {
Services.prefs.addObserver(GMPPrefs.KEY_EME_ENABLED, this, true);
Services.obs.addObserver(this, "EMEVideo:CDMMissing");
}
}
GMPWrapper.prototype = {
QueryInterface: ChromeUtils.generateQI([
"nsIObserver",
"nsISupportsWeakReference",
]),
// An active task that checks for plugin updates and installs them.
_updateTask: null,
_gmpPath: null,
_isUpdateCheckPending: false,
set gmpPath(aPath) {
this._gmpPath = aPath;
},
get gmpPath() {
if (!this._gmpPath && this.isInstalled) {
this._gmpPath = PathUtils.join(
Services.dirsvc.get("ProfD", Ci.nsIFile).path,
this._plugin.id,
GMPPrefs.getString(GMPPrefs.KEY_PLUGIN_VERSION, null, this._plugin.id)
);
}
return this._gmpPath;
},
get id() {
return this._plugin.id;
},
get libName() {
return this._plugin.libName;
},
get type() {
return "plugin";
},
get isGMPlugin() {
return true;
},
get name() {
return this._plugin.name;
},
get creator() {
return null;
},
get homepageURL() {
return this._plugin.homepageURL;
},
get description() {
return this._plugin.description;
},
get fullDescription() {
return null;
},
getFullDescription(doc) {
let plugin = this._rawPlugin;
let frag = doc.createDocumentFragment();
for (let [urlProp, labelId] of [
["learnMoreURL", GMP_LEARN_MORE],
[
"licenseURL",
this.id == WIDEVINE_L1_ID || this.id == WIDEVINE_L3_ID
? GMP_PRIVACY_INFO
: GMP_LICENSE_INFO,
],
]) {
if (plugin[urlProp]) {
let a = doc.createElementNS(XHTML, "a");
a.href = plugin[urlProp];
a.target = "_blank";
a.textContent = lazy.addonsBundle.formatValueSync(labelId);
if (frag.childElementCount) {
frag.append(
doc.createElementNS(XHTML, "br"),
doc.createElementNS(XHTML, "br")
);
}
frag.append(a);
}
}
return frag;
},
get version() {
return GMPPrefs.getString(
GMPPrefs.KEY_PLUGIN_VERSION,
null,
this._plugin.id
);
},
get isActive() {
return (
!this.appDisabled &&
!this.userDisabled &&
!GMPUtils.isPluginHidden(this._plugin)
);
},
get appDisabled() {
if (
this._plugin.isEME &&
!GMPPrefs.getBool(GMPPrefs.KEY_EME_ENABLED, true)
) {
// If "media.eme.enabled" is false, all EME plugins are disabled.
return true;
}
return false;
},
get userDisabled() {
return !GMPPrefs.getBool(
GMPPrefs.KEY_PLUGIN_ENABLED,
true,
this._plugin.id
);
},
set userDisabled(aVal) {
GMPPrefs.setBool(
GMPPrefs.KEY_PLUGIN_ENABLED,
aVal === false,
this._plugin.id
);
},
async enable() {
this.userDisabled = false;
},
async disable() {
this.userDisabled = true;
},
get blocklistState() {
return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
},
get size() {
return 0;
},
get scope() {
return lazy.AddonManager.SCOPE_APPLICATION;
},
get pendingOperations() {
return lazy.AddonManager.PENDING_NONE;
},
get operationsRequiringRestart() {
return lazy.AddonManager.OP_NEEDS_RESTART_NONE;
},
get permissions() {
let permissions = 0;
if (!this.appDisabled) {
permissions |= lazy.AddonManager.PERM_CAN_UPGRADE;
permissions |= this.userDisabled
? lazy.AddonManager.PERM_CAN_ENABLE
: lazy.AddonManager.PERM_CAN_DISABLE;
}
return permissions;
},
get updateDate() {
let time = Number(
GMPPrefs.getInt(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, 0, this._plugin.id)
);
if (this.isInstalled) {
return new Date(time * 1000);
}
return null;
},
get isCompatible() {
return true;
},
get isPlatformCompatible() {
return true;
},
get providesUpdatesSecurely() {
return true;
},
get foreignInstall() {
return false;
},
get installTelemetryInfo() {
return { source: "gmp-plugin" };
},
isCompatibleWith() {
return true;
},
get applyBackgroundUpdates() {
if (!GMPPrefs.isSet(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, this._plugin.id)) {
return lazy.AddonManager.AUTOUPDATE_DEFAULT;
}
return GMPPrefs.getBool(
GMPPrefs.KEY_PLUGIN_AUTOUPDATE,
true,
this._plugin.id
)
? lazy.AddonManager.AUTOUPDATE_ENABLE
: lazy.AddonManager.AUTOUPDATE_DISABLE;
},
set applyBackgroundUpdates(aVal) {
if (aVal == lazy.AddonManager.AUTOUPDATE_DEFAULT) {
GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, this._plugin.id);
} else if (aVal == lazy.AddonManager.AUTOUPDATE_ENABLE) {
GMPPrefs.setBool(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, true, this._plugin.id);
} else if (aVal == lazy.AddonManager.AUTOUPDATE_DISABLE) {
GMPPrefs.setBool(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, false, this._plugin.id);
}
},
/**
* Called by the addon manager to update GMP addons. For example this will be
* used if a user manually checks for GMP plugin updates by using the
* menu in about:addons.
*
* This function is not used if MediaKeySystemAccess is requested and
* Widevine is not yet installed, or if the user toggles prefs to enable EME.
* For the function used in those cases see `checkForUpdates`.
*/
findUpdates(aListener, aReason) {
this._log.trace(
"findUpdates() - " + this._plugin.id + " - reason=" + aReason
);
// In the case of GMP addons we do not wish to implement AddonInstall, as
// we don't want to display information as in a normal addon install such
// as a download progress bar. As such, we short circuit our
// listeners by indicating that no updates exist (though some may).
lazy.AddonManagerPrivate.callNoUpdateListeners(this, aListener);
if (aReason === lazy.AddonManager.UPDATE_WHEN_PERIODIC_UPDATE) {
if (!lazy.AddonManager.shouldAutoUpdate(this)) {
this._log.trace(
"findUpdates() - " + this._plugin.id + " - no autoupdate"
);
return Promise.resolve(false);
}
let secSinceLastCheck =
Date.now() / 1000 -
Services.prefs.getIntPref(GMPPrefs.KEY_UPDATE_LAST_CHECK, 0);
if (secSinceLastCheck <= SEC_IN_A_DAY) {
this._log.trace(
"findUpdates() - " +
this._plugin.id +
" - last check was less then a day ago"
);
return Promise.resolve(false);
}
} else if (aReason !== lazy.AddonManager.UPDATE_WHEN_USER_REQUESTED) {
this._log.trace(
"findUpdates() - " +
this._plugin.id +
" - the given reason to update is not supported"
);
return Promise.resolve(false);
}
if (this._updateTask !== null) {
this._log.trace(
"findUpdates() - " + this._plugin.id + " - update task already running"
);
return this._updateTask;
}
this._updateTask = (async () => {
this._log.trace("findUpdates() - updateTask");
try {
let installManager = new lazy.GMPInstallManager();
let res = await installManager.checkForAddons();
let update = res.addons.find(addon => addon.id === this._plugin.id);
if (update && update.isValid && !update.isInstalled) {
this._log.trace(
"findUpdates() - found update for " +
this._plugin.id +
", installing"
);
await installManager.installAddon(update);
} else {
this._log.trace("findUpdates() - no updates for " + this._plugin.id);
}
this._log.info(
"findUpdates() - updateTask succeeded for " + this._plugin.id
);
} catch (e) {
this._log.error(
"findUpdates() - updateTask for " + this._plugin.id + " threw",
e
);
throw e;
} finally {
this._updateTask = null;
}
return true;
})();
return this._updateTask;
},
get pluginLibraries() {
if (this.isInstalled) {
let path = this.version;
return [path];
}
return [];
},
get pluginFullpath() {
if (this.isInstalled) {
let path = PathUtils.join(
Services.dirsvc.get("ProfD", Ci.nsIFile).path,
this._plugin.id,
this.version
);
return [path];
}
return [];
},
get isInstalled() {
return this.version && !!this.version.length;
},
_handleEnabledChanged() {
this._log.info(
"_handleEnabledChanged() id=" +
this._plugin.id +
" isActive=" +
this.isActive
);
lazy.AddonManagerPrivate.callAddonListeners(
this.isActive ? "onEnabling" : "onDisabling",
this,
false
);
if (this._gmpPath) {
if (this.isActive) {
this._log.info(
"onPrefEnabledChanged() - adding gmp directory " + this._gmpPath
);
lazy.gmpService.addPluginDirectory(this._gmpPath);
} else {
this._log.info(
"onPrefEnabledChanged() - removing gmp directory " + this._gmpPath
);
lazy.gmpService.removePluginDirectory(this._gmpPath);
}
}
lazy.AddonManagerPrivate.callAddonListeners(
this.isActive ? "onEnabled" : "onDisabled",
this
);
},
onPrefEMEGlobalEnabledChanged() {
this._log.info(
"onPrefEMEGlobalEnabledChanged() id=" +
this._plugin.id +
" appDisabled=" +
this.appDisabled +
" isActive=" +
this.isActive +
" hidden=" +
GMPUtils.isPluginHidden(this._plugin)
);
lazy.AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, [
"appDisabled",
]);
// If EME or the GMP itself are disabled, uninstall the GMP.
// Otherwise, check for updates, so we download and install the GMP.
if (this.appDisabled) {
this.uninstallPlugin();
} else if (!GMPUtils.isPluginHidden(this._plugin)) {
lazy.AddonManagerPrivate.callInstallListeners(
"onExternalInstall",
null,
this,
null,
false
);
lazy.AddonManagerPrivate.callAddonListeners("onInstalling", this, false);
lazy.AddonManagerPrivate.callAddonListeners("onInstalled", this);
this.checkForUpdates(GMP_CHECK_DELAY);
}
if (!this.userDisabled) {
this._handleEnabledChanged();
}
},
/**
* This is called if prefs are changed to enable EME, or if Widevine
* MediaKeySystemAccess is requested but the Widevine CDM is not installed.
*
* For the function used by the addon manager see `findUpdates`.
*/
checkForUpdates(delay) {
if (this._isUpdateCheckPending) {
return;
}
this._isUpdateCheckPending = true;
GMPPrefs.reset(GMPPrefs.KEY_UPDATE_LAST_CHECK, null);
// Delay this in case the user changes his mind and doesn't want to
// enable EME after all.
lazy.setTimeout(() => {
if (!this.appDisabled) {
let gmpInstallManager = new lazy.GMPInstallManager();
// We don't really care about the results, if someone is interested
// they can check the log.
gmpInstallManager.simpleCheckAndInstall().catch(() => {});
}
this._isUpdateCheckPending = false;
}, delay);
},
onPrefEnabledChanged() {
if (!this._plugin.isEME || !this.appDisabled) {
this._handleEnabledChanged();
}
},
onPrefVersionChanged() {
lazy.AddonManagerPrivate.callAddonListeners("onUninstalling", this, false);
if (this._gmpPath) {
this._log.info(
"onPrefVersionChanged() - unregistering gmp directory " + this._gmpPath
);
lazy.gmpService.removeAndDeletePluginDirectory(
this._gmpPath,
true /* can defer */
);
}
lazy.AddonManagerPrivate.callAddonListeners("onUninstalled", this);
lazy.AddonManagerPrivate.callInstallListeners(
"onExternalInstall",
null,
this,
null,
false
);
lazy.AddonManagerPrivate.callAddonListeners("onInstalling", this, false);
this._gmpPath = null;
if (this.isInstalled) {
this._gmpPath = PathUtils.join(
Services.dirsvc.get("ProfD", Ci.nsIFile).path,
this._plugin.id,
GMPPrefs.getString(GMPPrefs.KEY_PLUGIN_VERSION, null, this._plugin.id)
);
}
if (this._gmpPath && this.isActive) {
this._log.info(
"onPrefVersionChanged() - registering gmp directory " + this._gmpPath
);
lazy.gmpService.addPluginDirectory(this._gmpPath);
}
lazy.AddonManagerPrivate.callAddonListeners("onInstalled", this);
},
observe(subject, topic, data) {
if (topic == "nsPref:changed") {
let pref = data;
if (
pref ==
GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_ENABLED, this._plugin.id)
) {
this.onPrefEnabledChanged();
} else if (
pref ==
GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_VERSION, this._plugin.id)
) {
this.onPrefVersionChanged();
} else if (pref == GMPPrefs.KEY_EME_ENABLED) {
this.onPrefEMEGlobalEnabledChanged();
}
} else if (topic == "EMEVideo:CDMMissing") {
this.checkForUpdates(0);
}
},
uninstallPlugin() {
lazy.AddonManagerPrivate.callAddonListeners("onUninstalling", this, false);
if (this.gmpPath) {
this._log.info(
"uninstallPlugin() - unregistering gmp directory " + this.gmpPath
);
lazy.gmpService.removeAndDeletePluginDirectory(this.gmpPath);
}
GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_VERSION, this.id);
GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_HASHVALUE, this.id);
GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_ABI, this.id);
GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, this.id);
lazy.AddonManagerPrivate.callAddonListeners("onUninstalled", this);
},
shutdown() {
Services.prefs.removeObserver(
GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_ENABLED, this._plugin.id),
this
);
Services.prefs.removeObserver(
GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_VERSION, this._plugin.id),
this
);
if (this._plugin.isEME) {
Services.prefs.removeObserver(GMPPrefs.KEY_EME_ENABLED, this);
Services.obs.removeObserver(this, "EMEVideo:CDMMissing");
}
return this._updateTask;
},
_arePluginFilesOnDisk() {
let fileExists = function (aGmpPath, aFileName) {
let f = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
let path = PathUtils.join(aGmpPath, aFileName);
f.initWithPath(path);
return f.exists();
};
let libName =
AppConstants.DLL_PREFIX + this._plugin.libName + AppConstants.DLL_SUFFIX;
let infoName;
if (
this._plugin.id == WIDEVINE_L1_ID ||
this._plugin.id == WIDEVINE_L3_ID
) {
infoName = "manifest.json";
} else {
infoName = this._plugin.id.substring(4) + ".info";
}
return (
fileExists(this.gmpPath, libName) && fileExists(this.gmpPath, infoName)
);
},
validate() {
if (!this.isInstalled) {
// Not installed -> Valid.
return {
installed: false,
valid: true,
};
}
let expectedABI = GMPUtils._expectedABI(this._plugin);
let abi = GMPPrefs.getString(
GMPPrefs.KEY_PLUGIN_ABI,
expectedABI,
this._plugin.id
);
if (abi != expectedABI) {
// ABI doesn't match. Possibly this is a profile migrated across platforms
// or from 32 -> 64 bit.
return {
installed: true,
mismatchedABI: true,
valid: false,
};
}
// Installed -> Check if files are missing.
let filesOnDisk = this._arePluginFilesOnDisk();
return {
installed: true,
valid: filesOnDisk,
};
},
};
var GMPProvider = {
get name() {
return "GMPProvider";
},
_plugins: null,
startup() {
configureLogging();
this._log = lazy.Log.repository.getLoggerWithMessagePrefix(
"Toolkit.GMP",
"GMPProvider."
);
this.buildPluginList();
this.ensureProperCDMInstallState();
Services.prefs.addObserver(GMPPrefs.KEY_LOG_BASE, configureLogging);
for (let plugin of this._plugins.values()) {
let wrapper = plugin.wrapper;
let gmpPath = wrapper.gmpPath;
let isEnabled = wrapper.isActive;
this._log.trace(
"startup - enabled=" + isEnabled + ", gmpPath=" + gmpPath
);
if (gmpPath && isEnabled) {
let validation = wrapper.validate();
if (validation.mismatchedABI) {
this._log.info(
"startup - gmp " + plugin.id + " mismatched ABI, uninstalling"
);
wrapper.uninstallPlugin();
continue;
}
if (!validation.valid) {
this._log.info(
"startup - gmp " + plugin.id + " invalid, uninstalling"
);
wrapper.uninstallPlugin();
continue;
}
this._log.info("startup - adding gmp directory " + gmpPath);
try {
lazy.gmpService.addPluginDirectory(gmpPath);
} catch (e) {
if (e.name != "NS_ERROR_NOT_AVAILABLE") {
throw e;
}
this._log.warn(
"startup - adding gmp directory failed with " +
e.name +
" - sandboxing not available?",
e
);
}
}
}
try {
let greDir = Services.dirsvc.get(NS_GRE_DIR, Ci.nsIFile);
let path = greDir.path;
if (
GMPUtils._isWindowsOnARM64() &&
GMPPrefs.getBool(
GMPPrefs.KEY_PLUGIN_ALLOW_X64_ON_ARM64,
true,
CLEARKEY_PLUGIN_ID
)
) {
path = PathUtils.join(path, "i686");
}
let clearkeyPath = PathUtils.join(
path,
CLEARKEY_PLUGIN_ID,
CLEARKEY_VERSION
);
this._log.info("startup - adding clearkey CDM directory " + clearkeyPath);
lazy.gmpService.addPluginDirectory(clearkeyPath);
} catch (e) {
this._log.warn("startup - adding clearkey CDM failed", e);
}
},
shutdown() {
this._log.trace("shutdown");
Services.prefs.removeObserver(GMPPrefs.KEY_LOG_BASE, configureLogging);
let shutdownTask = (async () => {
this._log.trace("shutdown - shutdownTask");
let shutdownSucceeded = true;
for (let plugin of this._plugins.values()) {
try {
await plugin.wrapper.shutdown();
} catch (e) {
shutdownSucceeded = false;
}
}
this._plugins = null;
if (!shutdownSucceeded) {
throw new Error("Shutdown failed");
}
})();
return shutdownTask;
},
async getAddonByID(aId) {
if (!this.isEnabled) {
return null;
}
let plugin = this._plugins.get(aId);
if (plugin && !GMPUtils.isPluginHidden(plugin)) {
return plugin.wrapper;
}
return null;
},
async getAddonsByTypes(aTypes) {
if (!this.isEnabled || (aTypes && !aTypes.includes("plugin"))) {
return [];
}
let results = Array.from(this._plugins.values())
.filter(p => !GMPUtils.isPluginHidden(p))
.map(p => p.wrapper);
return results;
},
get isEnabled() {
return lazy.gmpProviderEnabled;
},
buildPluginList() {
this._plugins = new Map();
for (let aPlugin of GMP_PLUGINS) {
let plugin = {
id: aPlugin.id,
name: lazy.addonsBundle.formatValueSync(aPlugin.name),
description: lazy.addonsBundle.formatValueSync(aPlugin.description),
libName: aPlugin.libName,
homepageURL: aPlugin.homepageURL,
optionsURL: aPlugin.optionsURL,
wrapper: null,
isEME: aPlugin.isEME,
};
plugin.wrapper = new GMPWrapper(plugin, aPlugin);
this._plugins.set(plugin.id, plugin);
}
},
ensureProperCDMInstallState() {
if (!GMPPrefs.getBool(GMPPrefs.KEY_EME_ENABLED, true)) {
for (let plugin of this._plugins.values()) {
if (plugin.isEME && plugin.wrapper.isInstalled) {
lazy.gmpService.addPluginDirectory(plugin.wrapper.gmpPath);
plugin.wrapper.uninstallPlugin();
}
}
}
},
observe(subject, topic) {
if (topic == FIRST_CONTENT_PROCESS_TOPIC) {
lazy.AddonManagerPrivate.registerProvider(GMPProvider, ["plugin"]);
Services.obs.notifyObservers(null, "gmp-provider-registered");
Services.obs.removeObserver(this, FIRST_CONTENT_PROCESS_TOPIC);
}
},
addObserver() {
Services.obs.addObserver(this, FIRST_CONTENT_PROCESS_TOPIC);
},
};
GMPProvider.addObserver();
// For test use only.
export const GMPTestUtils = {
/**
* Used to override the GMP service with a mock.
*
* @param {object} mockService
* The mocked gmpService object.
* @param {function} callback
* Method called with the overridden gmpService. The override
* is undone after the callback returns.
*/
async overrideGmpService(mockService, callback) {
let originalGmpService = lazy.gmpService;
lazy.gmpService = mockService;
try {
return await callback();
} finally {
lazy.gmpService = originalGmpService;
}
},
};