"use strict"; /* exported MockAlertsService */ function mockServicesChromeScript() { /* eslint-env mozilla/chrome-script */ const MOCK_ALERTS_CID = Components.ID( "{48068bc2-40ab-4904-8afd-4cdfb3a385f3}" ); const ALERTS_SERVICE_CONTRACT_ID = "@mozilla.org/alerts-service;1"; const { setTimeout } = ChromeUtils.importESModule( "resource://gre/modules/Timer.sys.mjs" ); const registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); let activeNotifications = Object.create(null); const mockAlertsService = { showPersistentNotification: function ( persistentData, alert, alertListener ) { this.showAlert(alert, alertListener); }, showAlert: function (alert, listener) { activeNotifications[alert.name] = { listener: listener, cookie: alert.cookie, title: alert.title, }; // fake async alert show event if (listener) { setTimeout(function () { listener.observe(null, "alertshow", alert.cookie); }, 100); } }, showAlertNotification: function ( imageUrl, title, text, textClickable, cookie, alertListener, name ) { this.showAlert( { name: name, cookie: cookie, title: title, }, alertListener ); }, closeAlert: function (name) { let alertNotification = activeNotifications[name]; if (alertNotification) { if (alertNotification.listener) { alertNotification.listener.observe( null, "alertfinished", alertNotification.cookie ); } delete activeNotifications[name]; } }, QueryInterface: ChromeUtils.generateQI(["nsIAlertsService"]), createInstance: function (iid) { return this.QueryInterface(iid); }, }; registrar.registerFactory( MOCK_ALERTS_CID, "alerts service", ALERTS_SERVICE_CONTRACT_ID, mockAlertsService ); function clickNotifications(doClose) { // Until we need to close a specific notification, just click them all. for (let [name, notification] of Object.entries(activeNotifications)) { let { listener, cookie } = notification; listener.observe(null, "alertclickcallback", cookie); if (doClose) { mockAlertsService.closeAlert(name); } } } function closeAllNotifications() { for (let alertName of Object.keys(activeNotifications)) { mockAlertsService.closeAlert(alertName); } } const { addMessageListener, sendAsyncMessage } = this; addMessageListener("mock-alert-service:unregister", () => { closeAllNotifications(); activeNotifications = null; registrar.unregisterFactory(MOCK_ALERTS_CID, mockAlertsService); sendAsyncMessage("mock-alert-service:unregistered"); }); addMessageListener( "mock-alert-service:click-notifications", clickNotifications ); addMessageListener( "mock-alert-service:close-notifications", closeAllNotifications ); sendAsyncMessage("mock-alert-service:registered"); } const MockAlertsService = { async register() { if (this._chromeScript) { throw new Error("MockAlertsService already registered"); } this._chromeScript = SpecialPowers.loadChromeScript( mockServicesChromeScript ); await this._chromeScript.promiseOneMessage("mock-alert-service:registered"); }, async unregister() { if (!this._chromeScript) { throw new Error("MockAlertsService not registered"); } this._chromeScript.sendAsyncMessage("mock-alert-service:unregister"); return this._chromeScript .promiseOneMessage("mock-alert-service:unregistered") .then(() => { this._chromeScript.destroy(); this._chromeScript = null; }); }, async clickNotifications() { // Most implementations of the nsIAlertsService automatically close upon click. await this._chromeScript.sendAsyncMessage( "mock-alert-service:click-notifications", true ); }, async clickNotificationsWithoutClose() { // The implementation on macOS does not automatically close the notification. await this._chromeScript.sendAsyncMessage( "mock-alert-service:click-notifications", false ); }, async closeNotifications() { await this._chromeScript.sendAsyncMessage( "mock-alert-service:close-notifications" ); }, };