/* 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/. */ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { RemoteSettings: "resource://services-settings/remote-settings.sys.mjs", }); ChromeUtils.defineLazyGetter(lazy, "console", () => { return console.createInstance({ prefix: "EME" }); }); const COLLECTION_NAME = "mfcdm-origins-list"; // These are defined in nsIWindowsMediaFoundationCDMOriginsListService.idl const ORIGIN_BLOCKED = 0; const ORIGIN_ALLOWED = 1; function log(message) { const LOGGING_ENABLED = Services.env.get("MOZ_LOG")?.includes("EME"); if (LOGGING_ENABLED) { lazy.console.debug( `WindowsMediaFoundationCDMOriginsListService : ${message}\n` ); } } /** * Represents an entry in the origin status list. * * @param {string} origin The origin URL. * @param {boolean} status The status of the origin. */ class OriginStatusEntry { constructor(origin, status) { this._origin = origin; this._status = status; } get origin() { return this._origin; } get status() { return this._status; } } OriginStatusEntry.prototype.classID = Components.ID( "{b1d5e2f2-8d65-41f3-86e8-9c6c21f2486d}" ); OriginStatusEntry.prototype.QueryInterface = ChromeUtils.generateQI([ "nsIOriginStatusEntry", ]); export function WindowsMediaFoundationCDMOriginsListService() { log("Created service"); Services.obs.addObserver(this, "xpcom-shutdown"); } WindowsMediaFoundationCDMOriginsListService.prototype = { classID: Components.ID("{a9b28d1f-7e11-4c5f-85a0-4fd39db1f7c9}"), QueryInterface: ChromeUtils.generateQI([ "nsIWindowsMediaFoundationCDMOriginsListService", ]), _rs: null, _entriesReady: false, // this callback will make content parents to broadcast the origin status entry. _callbacks: [], // An array of OriginStatusEntry. _originsList: null, async _getOriginsListFromRemoteService() { log(`Create the remote service and fetching data`); if (this._entriesReady) { return; } this._rs = lazy.RemoteSettings(COLLECTION_NAME); this._rs.on("sync", async event => { let { data: { current }, } = event; this._onUpdateEntries(current || []); }); let entries; try { entries = await this._rs.get(); } catch (e) { log(`Error fetching origins list: ${e}`); } this._onUpdateEntries(entries || []); this._entriesReady = true; }, _onUpdateEntries(aEntries) { log(`Update entries, aEntries=${JSON.stringify(aEntries, null, 2)}`); const entries = Cc["@mozilla.org/array;1"].createInstance( Ci.nsIMutableArray ); for (let entry of aEntries) { log(`Entry name: ${entry.origin}, allowed: ${entry.allowed}`); const status = entry.allowed ? ORIGIN_ALLOWED : ORIGIN_BLOCKED; const originStatusEntry = new OriginStatusEntry(entry.origin, status); entries.appendElement(originStatusEntry); } this._originsList = entries; if (this._callbacks.length) { log("Processing entries for all callbacks."); for (let callback of this._callbacks) { try { callback.onOriginsListLoaded(entries); } catch (e) { log(`Error calling callback: ${e}`); } } } }, // xpcom-shutdown observer observe(_aSubject, aTopic, _aData) { if (aTopic == "xpcom-shutdown") { Services.obs.removeObserver(this, "xpcom-shutdown"); this._callbacks.length = 0; this._callbacks = null; this._originsList = null; if (this._rs) { this._rs.off("sync"); this._rs = null; } } }, // Below methods are for nsIWindowsMediaFoundationCDMOriginsListService setCallback(aCallback) { log(`SetCallback`); this._callbacks.push(aCallback); if (!this._entriesReady) { this._getOriginsListFromRemoteService(); } else { log("Entries already ready. Immediately notifying new callback."); aCallback.onOriginsListLoaded(this._originsList); } }, removeCallback(aCallback) { log(`RemoveCallback`); const index = this._callbacks.indexOf(aCallback); if (index !== -1) { this._callbacks.splice(index, 1); log(`Callback removed successfully.`); } else { log(`Callback not found.`); } }, };