From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../asrouter/modules/PageEventManager.sys.mjs | 135 +++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 browser/components/asrouter/modules/PageEventManager.sys.mjs (limited to 'browser/components/asrouter/modules/PageEventManager.sys.mjs') diff --git a/browser/components/asrouter/modules/PageEventManager.sys.mjs b/browser/components/asrouter/modules/PageEventManager.sys.mjs new file mode 100644 index 0000000000..44f1293385 --- /dev/null +++ b/browser/components/asrouter/modules/PageEventManager.sys.mjs @@ -0,0 +1,135 @@ +/* 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/. */ + +/** + * Methods for setting up and tearing down page event listeners. These are used + * to dismiss Feature Callouts when the callout's anchor element is clicked. + */ +export class PageEventManager { + /** + * A set of parameters defining a page event listener. + * @typedef {Object} PageEventListenerParams + * @property {String} type Event type string e.g. `click` + * @property {String} [selectors] Target selector, e.g. `tag.class, #id[attr]` + * @property {PageEventListenerOptions} [options] addEventListener options + * + * @typedef {Object} PageEventListenerOptions + * @property {Boolean} [capture] Use event capturing phase? + * @property {Boolean} [once] Remove listener after first event? + * @property {Boolean} [preventDefault] Inverted value for `passive` option + * @property {Number} [interval] Used only for `timeout` and `interval` event + * types. These don't set up real event listeners, but instead invoke the + * action on a timer. + * + * @typedef {Object} PageEventListener + * @property {Function} callback Function to call when event is triggered + * @property {AbortController} controller Handle for aborting the listener + * + * @typedef {Object} PageEvent + * @property {String} type Event type string e.g. `click` + * @property {Element} [target] Event target + */ + + /** + * Maps event listener params to their PageEventListeners, so they can be + * called and cancelled. + * @type {Map} + */ + _listeners = new Map(); + + /** + * @param {Window} win Window containing the document to listen to + */ + constructor(win) { + this.win = win; + this.doc = win.document; + } + + /** + * Adds a page event listener. + * @param {PageEventListenerParams} params + * @param {Function} callback Function to call when event is triggered + */ + on(params, callback) { + if (this._listeners.has(params)) { + return; + } + const { type, selectors, options = {} } = params; + const listener = { callback }; + if (selectors) { + const controller = new AbortController(); + const opt = { + capture: !!options.capture, + passive: !options.preventDefault, + signal: controller.signal, + }; + const targets = this.doc.querySelectorAll(selectors); + for (const target of targets) { + target.addEventListener(type, callback, opt); + } + listener.controller = controller; + } else if (["timeout", "interval"].includes(type) && options.interval) { + let interval; + const abort = () => this.win.clearInterval(interval); + const onInterval = () => { + callback({ type, target: type }); + if (type === "timeout") { + abort(); + } + }; + interval = this.win.setInterval(onInterval, options.interval); + listener.callback = onInterval; + listener.controller = { abort }; + } + this._listeners.set(params, listener); + } + + /** + * Removes a page event listener. + * @param {PageEventListenerParams} params + */ + off(params) { + const listener = this._listeners.get(params); + if (!listener) { + return; + } + listener.controller?.abort(); + this._listeners.delete(params); + } + + /** + * Adds a page event listener that is removed after the first event. + * @param {PageEventListenerParams} params + * @param {Function} callback Function to call when event is triggered + */ + once(params, callback) { + const wrappedCallback = (...args) => { + this.off(params); + callback(...args); + }; + this.on(params, wrappedCallback); + } + + /** + * Removes all page event listeners. + */ + clear() { + for (const listener of this._listeners.values()) { + listener.controller?.abort(); + } + this._listeners.clear(); + } + + /** + * Calls matching page event listeners. A way to dispatch a "fake" event. + * @param {PageEvent} event + */ + emit(event) { + for (const [params, listener] of this._listeners) { + if (params.type === event.type) { + listener.callback(event); + } + } + } +} -- cgit v1.2.3