/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const { GMPTestUtils } = ChromeUtils.importESModule( "resource://gre/modules/addons/GMPProvider.sys.mjs" ); const { GMPInstallManager } = ChromeUtils.importESModule( "resource://gre/modules/GMPInstallManager.sys.mjs" ); const { GMPPrefs, GMP_PLUGIN_IDS, OPEN_H264_ID, WIDEVINE_L1_ID, WIDEVINE_L3_ID, } = ChromeUtils.importESModule("resource://gre/modules/GMPUtils.sys.mjs"); const { UpdateUtils } = ChromeUtils.importESModule( "resource://gre/modules/UpdateUtils.sys.mjs" ); ChromeUtils.defineLazyGetter( this, "addonsBundle", () => new Localization(["toolkit/about/aboutAddons.ftl"]) ); var gMockAddons = new Map(); var gMockEmeAddons = new Map(); const mockH264Addon = Object.freeze({ id: OPEN_H264_ID, isValid: true, isInstalled: false, nameId: "plugins-openh264-name", descriptionId: "plugins-openh264-description", libName: "gmpopenh264", usedFallback: true, }); gMockAddons.set(mockH264Addon.id, mockH264Addon); const mockWidevineL1Addon = Object.freeze({ id: WIDEVINE_L1_ID, isValid: true, isInstalled: false, nameId: "plugins-widevine-name", descriptionId: "plugins-widevine-description", libName: "Google.Widevine.CDM", usedFallback: true, }); gMockAddons.set(mockWidevineL1Addon.id, mockWidevineL1Addon); gMockEmeAddons.set(mockWidevineL1Addon.id, mockWidevineL1Addon); const mockWidevineAddon = Object.freeze({ id: WIDEVINE_L3_ID, isValid: true, isInstalled: false, nameId: "plugins-widevine-name", descriptionId: "plugins-widevine-description", libName: "widevinecdm", usedFallback: true, }); gMockAddons.set(mockWidevineAddon.id, mockWidevineAddon); gMockEmeAddons.set(mockWidevineAddon.id, mockWidevineAddon); var gInstalledAddonId = ""; var gPrefs = Services.prefs; var gGetKey = GMPPrefs.getPrefKey; const MockGMPInstallManagerPrototype = { checkForAddons: () => Promise.resolve({ addons: [...gMockAddons.values()], }), installAddon: addon => { gInstalledAddonId = addon.id; return Promise.resolve(); }, }; add_setup(async () => { Assert.deepEqual( GMP_PLUGIN_IDS, Array.from(gMockAddons.keys()), "set of mock addons matches the actual set of plugins" ); createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); // The GMPProvider does not register until the first content process // is launched, so we simulate that by firing this notification. Services.obs.notifyObservers(null, "ipc:first-content-process-created"); await promiseStartupManager(); gPrefs.setBoolPref(GMPPrefs.KEY_LOGGING_DUMP, true); gPrefs.setIntPref(GMPPrefs.KEY_LOGGING_LEVEL, 0); gPrefs.setBoolPref(GMPPrefs.KEY_EME_ENABLED, true); for (let addon of gMockAddons.values()) { gPrefs.setBoolPref(gGetKey(GMPPrefs.KEY_PLUGIN_VISIBLE, addon.id), true); gPrefs.setBoolPref( gGetKey(GMPPrefs.KEY_PLUGIN_FORCE_SUPPORTED, addon.id), true ); } }); add_task(async function test_notInstalled() { for (let addon of gMockAddons.values()) { gPrefs.setCharPref(gGetKey(GMPPrefs.KEY_PLUGIN_VERSION, addon.id), ""); gPrefs.setBoolPref(gGetKey(GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), false); } let addons = await promiseAddonsByIDs([...gMockAddons.keys()]); Assert.equal(addons.length, gMockAddons.size); for (let addon of addons) { Assert.ok(!addon.isInstalled); Assert.equal(addon.type, "plugin"); Assert.equal(addon.version, ""); let mockAddon = gMockAddons.get(addon.id); Assert.notEqual(mockAddon, null); let name = await addonsBundle.formatValue(mockAddon.nameId); Assert.equal(addon.name, name); let description = await addonsBundle.formatValue(mockAddon.descriptionId); Assert.equal(addon.description, description); Assert.ok(!addon.isActive); Assert.ok(!addon.appDisabled); Assert.ok(addon.userDisabled); Assert.equal( addon.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED ); Assert.equal(addon.scope, AddonManager.SCOPE_APPLICATION); Assert.equal(addon.pendingOperations, AddonManager.PENDING_NONE); Assert.equal(addon.operationsRequiringRestart, AddonManager.PENDING_NONE); Assert.equal( addon.permissions, AddonManager.PERM_CAN_UPGRADE | AddonManager.PERM_CAN_ENABLE ); Assert.equal(addon.updateDate, null); Assert.ok(addon.isCompatible); Assert.ok(addon.isPlatformCompatible); Assert.ok(addon.providesUpdatesSecurely); Assert.ok(!addon.foreignInstall); let libraries = addon.pluginLibraries; Assert.ok(libraries); Assert.equal(libraries.length, 0); Assert.equal(addon.pluginFullpath, ""); } }); add_task(async function test_installed() { const TEST_DATE = new Date(2013, 0, 1, 12); const TEST_VERSION = "1.2.3.4"; const TEST_TIME_SEC = Math.round(TEST_DATE.getTime() / 1000); let addons = await promiseAddonsByIDs([...gMockAddons.keys()]); Assert.equal(addons.length, gMockAddons.size); for (let addon of addons) { let mockAddon = gMockAddons.get(addon.id); Assert.notEqual(mockAddon, null); let file = Services.dirsvc.get("ProfD", Ci.nsIFile); file.append(addon.id); file.append(TEST_VERSION); gPrefs.setBoolPref( gGetKey(GMPPrefs.KEY_PLUGIN_ENABLED, mockAddon.id), false ); gPrefs.setIntPref( gGetKey(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, mockAddon.id), TEST_TIME_SEC ); gPrefs.setCharPref( gGetKey(GMPPrefs.KEY_PLUGIN_VERSION, mockAddon.id), TEST_VERSION ); Assert.ok(addon.isInstalled); Assert.equal(addon.type, "plugin"); Assert.ok(!addon.isActive); Assert.ok(!addon.appDisabled); Assert.ok(addon.userDisabled); let name = await addonsBundle.formatValue(mockAddon.nameId); Assert.equal(addon.name, name); Assert.equal(addon.version, TEST_VERSION); Assert.equal( addon.permissions, AddonManager.PERM_CAN_UPGRADE | AddonManager.PERM_CAN_ENABLE ); Assert.equal(addon.updateDate.getTime(), TEST_TIME_SEC * 1000); let libraries = addon.pluginLibraries; Assert.ok(libraries); Assert.equal(libraries.length, 1); Assert.equal(libraries[0], TEST_VERSION); let fullpath = addon.pluginFullpath; Assert.equal(fullpath.length, 1); Assert.equal(fullpath[0], file.path); } }); add_task(async function test_enable() { let addons = await promiseAddonsByIDs([...gMockAddons.keys()]); Assert.equal(addons.length, gMockAddons.size); for (let addon of addons) { gPrefs.setBoolPref(gGetKey(GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), true); Assert.ok(addon.isActive); Assert.ok(!addon.appDisabled); Assert.ok(!addon.userDisabled); Assert.equal( addon.permissions, AddonManager.PERM_CAN_UPGRADE | AddonManager.PERM_CAN_DISABLE ); } }); add_task(async function test_globalEmeDisabled() { let addons = await promiseAddonsByIDs([...gMockEmeAddons.keys()]); Assert.equal(addons.length, gMockEmeAddons.size); gPrefs.setBoolPref(GMPPrefs.KEY_EME_ENABLED, false); for (let addon of addons) { Assert.ok(!addon.isActive); Assert.ok(addon.appDisabled); Assert.ok(!addon.userDisabled); Assert.equal(addon.permissions, 0); } gPrefs.setBoolPref(GMPPrefs.KEY_EME_ENABLED, true); }); add_task(async function test_autoUpdatePrefPersistance() { let addons = await promiseAddonsByIDs([...gMockAddons.keys()]); Assert.equal(addons.length, gMockAddons.size); for (let addon of addons) { let autoupdateKey = gGetKey(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, addon.id); gPrefs.clearUserPref(autoupdateKey); addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE; Assert.ok(!gPrefs.getBoolPref(autoupdateKey)); addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE; Assert.equal(addon.applyBackgroundUpdates, AddonManager.AUTOUPDATE_ENABLE); Assert.ok(gPrefs.getBoolPref(autoupdateKey)); addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT; Assert.ok(!gPrefs.prefHasUserValue(autoupdateKey)); } }); function createMockPluginFilesIfNeeded(aFile, aPlugin) { function createFile(aFileName) { let f = aFile.clone(); f.append(aFileName); if (!f.exists()) { f.create(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE); } } let libName = AppConstants.DLL_PREFIX + aPlugin.libName + AppConstants.DLL_SUFFIX; createFile(libName); if (aPlugin.id == WIDEVINE_L1_ID || aPlugin.id == WIDEVINE_L3_ID) { createFile("manifest.json"); } else { createFile(aPlugin.id.substring(4) + ".info"); } } add_task(async function test_pluginRegistration() { const TEST_VERSION = "1.2.3.4"; let addedPaths = []; let removedPaths = []; let clearPaths = () => { addedPaths = []; removedPaths = []; }; const MockGMPService = { addPluginDirectory: path => { if (!addedPaths.includes(path)) { addedPaths.push(path); } }, removePluginDirectory: path => { if (!removedPaths.includes(path)) { removedPaths.push(path); } }, removeAndDeletePluginDirectory: path => { if (!removedPaths.includes(path)) { removedPaths.push(path); } }, }; let profD = do_get_profile(); for (let addon of gMockAddons.values()) { await GMPTestUtils.overrideGmpService(MockGMPService, () => testAddon(addon) ); } async function testAddon(addon) { let file = profD.clone(); file.append(addon.id); file.append(TEST_VERSION); gPrefs.setBoolPref(gGetKey(GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), true); // Test that plugin registration fails if the plugin dynamic library and // info files are not present. gPrefs.setCharPref( gGetKey(GMPPrefs.KEY_PLUGIN_VERSION, addon.id), TEST_VERSION ); clearPaths(); await promiseRestartManager(); Assert.equal(addedPaths.indexOf(file.path), -1); Assert.deepEqual(removedPaths, [file.path]); // Create dummy GMP library/info files, and test that plugin registration // succeeds during startup, now that we've added GMP info/lib files. createMockPluginFilesIfNeeded(file, addon); gPrefs.setCharPref( gGetKey(GMPPrefs.KEY_PLUGIN_VERSION, addon.id), TEST_VERSION ); clearPaths(); await promiseRestartManager(); Assert.notEqual(addedPaths.indexOf(file.path), -1); Assert.deepEqual(removedPaths, []); // Setting the ABI to something invalid should cause plugin to be removed at startup. clearPaths(); gPrefs.setCharPref( gGetKey(GMPPrefs.KEY_PLUGIN_ABI, addon.id), "invalid-ABI" ); await promiseRestartManager(); Assert.equal(addedPaths.indexOf(file.path), -1); Assert.deepEqual(removedPaths, [file.path]); // Setting the ABI to expected ABI should cause registration at startup. clearPaths(); gPrefs.setCharPref( gGetKey(GMPPrefs.KEY_PLUGIN_VERSION, addon.id), TEST_VERSION ); gPrefs.setCharPref( gGetKey(GMPPrefs.KEY_PLUGIN_ABI, addon.id), UpdateUtils.ABI ); await promiseRestartManager(); Assert.notEqual(addedPaths.indexOf(file.path), -1); Assert.deepEqual(removedPaths, []); // Check that clearing the version doesn't trigger registration. clearPaths(); gPrefs.clearUserPref(gGetKey(GMPPrefs.KEY_PLUGIN_VERSION, addon.id)); Assert.deepEqual(addedPaths, []); Assert.deepEqual(removedPaths, [file.path]); // Restarting with no version set should not trigger registration. clearPaths(); await promiseRestartManager(); Assert.equal(addedPaths.indexOf(file.path), -1); Assert.equal(removedPaths.indexOf(file.path), -1); // Changing the pref mid-session should cause unregistration and registration. gPrefs.setCharPref( gGetKey(GMPPrefs.KEY_PLUGIN_VERSION, addon.id), TEST_VERSION ); clearPaths(); const TEST_VERSION_2 = "5.6.7.8"; let file2 = Services.dirsvc.get("ProfD", Ci.nsIFile); file2.append(addon.id); file2.append(TEST_VERSION_2); gPrefs.setCharPref( gGetKey(GMPPrefs.KEY_PLUGIN_VERSION, addon.id), TEST_VERSION_2 ); Assert.deepEqual(addedPaths, [file2.path]); Assert.deepEqual(removedPaths, [file.path]); // Disabling the plugin should cause unregistration. gPrefs.setCharPref( gGetKey(GMPPrefs.KEY_PLUGIN_VERSION, addon.id), TEST_VERSION ); clearPaths(); gPrefs.setBoolPref(gGetKey(GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), false); Assert.deepEqual(addedPaths, []); Assert.deepEqual(removedPaths, [file.path]); // Restarting with the plugin disabled should not cause registration. clearPaths(); await promiseRestartManager(); Assert.equal(addedPaths.indexOf(file.path), -1); Assert.equal(removedPaths.indexOf(file.path), -1); // Re-enabling the plugin should cause registration. clearPaths(); gPrefs.setBoolPref(gGetKey(GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), true); Assert.deepEqual(addedPaths, [file.path]); Assert.deepEqual(removedPaths, []); } }); add_task(async function test_periodicUpdate() { // The GMPInstallManager constructor has an empty body, // so replacing the prototype is safe. let originalInstallManager = GMPInstallManager.prototype; GMPInstallManager.prototype = MockGMPInstallManagerPrototype; let addons = await promiseAddonsByIDs([...gMockAddons.keys()]); Assert.equal(addons.length, gMockAddons.size); for (let addon of addons) { gPrefs.clearUserPref(gGetKey(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, addon.id)); addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE; gPrefs.setIntPref(GMPPrefs.KEY_UPDATE_LAST_CHECK, 0); let result = await addon.findUpdates( {}, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE ); Assert.strictEqual(result, false); addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE; gPrefs.setIntPref(GMPPrefs.KEY_UPDATE_LAST_CHECK, Date.now() / 1000 - 60); result = await addon.findUpdates( {}, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE ); Assert.strictEqual(result, false); const SEC_IN_A_DAY = 24 * 60 * 60; gPrefs.setIntPref( GMPPrefs.KEY_UPDATE_LAST_CHECK, Date.now() / 1000 - 2 * SEC_IN_A_DAY ); gInstalledAddonId = ""; result = await addon.findUpdates( {}, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE ); Assert.strictEqual(result, true); Assert.equal(gInstalledAddonId, addon.id); } GMPInstallManager.prototype = originalInstallManager; });