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 --- toolkit/components/prompts/src/PromptUtils.sys.mjs | 192 +++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 toolkit/components/prompts/src/PromptUtils.sys.mjs (limited to 'toolkit/components/prompts/src/PromptUtils.sys.mjs') diff --git a/toolkit/components/prompts/src/PromptUtils.sys.mjs b/toolkit/components/prompts/src/PromptUtils.sys.mjs new file mode 100644 index 0000000000..8c1b0ff992 --- /dev/null +++ b/toolkit/components/prompts/src/PromptUtils.sys.mjs @@ -0,0 +1,192 @@ +/* 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/. */ + +export var PromptUtils = { + // Fire a dialog open/close event. Used by tabbrowser to focus the + // tab which is triggering a prompt. + // For remote dialogs, we pass in a different DOM window and a separate + // target. If the caller doesn't pass in the target, then we'll simply use + // the passed-in DOM window. + // The detail may contain information about the principal on which the + // prompt is triggered, as well as whether or not this is a tabprompt + // (ie tabmodal alert/prompt/confirm and friends) + fireDialogEvent(domWin, eventName, maybeTarget, detail) { + let target = maybeTarget || domWin; + let eventOptions = { cancelable: true, bubbles: true }; + if (detail) { + eventOptions.detail = detail; + } + let event = new domWin.CustomEvent(eventName, eventOptions); + let winUtils = domWin.windowUtils; + winUtils.dispatchEventToChromeOnly(target, event); + }, + + objectToPropBag(obj) { + let bag = Cc["@mozilla.org/hash-property-bag;1"].createInstance( + Ci.nsIWritablePropertyBag2 + ); + bag.QueryInterface(Ci.nsIWritablePropertyBag); + + for (let propName in obj) { + bag.setProperty(propName, obj[propName]); + } + + return bag; + }, + + propBagToObject(propBag, obj) { + // Here we iterate over the object's original properties, not the bag + // (ie, the prompt can't return more/different properties than were + // passed in). This just helps ensure that the caller provides default + // values, lest the prompt forget to set them. + for (let propName in obj) { + obj[propName] = propBag.getProperty(propName); + } + }, +}; + +/** + * This helper handles the enabling/disabling of dialogs that might + * be subject to fast-clicking attacks. It handles the initial delayed + * enabling of the dialog, as well as disabling it on blur and reapplying + * the delay when the dialog regains focus. + * + * @param enableDialog A custom function to be called when the dialog + * is to be enabled. + * @param diableDialog A custom function to be called when the dialog + * is to be disabled. + * @param focusTarget The window used to watch focus/blur events. + */ +export var EnableDelayHelper = function ({ + enableDialog, + disableDialog, + focusTarget, +}) { + this.enableDialog = makeSafe(enableDialog); + this.disableDialog = makeSafe(disableDialog); + this.focusTarget = focusTarget; + + this.disableDialog(); + + this.focusTarget.addEventListener("blur", this); + this.focusTarget.addEventListener("focus", this); + // While the user key-repeats, we want to renew the timer until keyup: + this.focusTarget.addEventListener("keyup", this, true); + this.focusTarget.addEventListener("keydown", this, true); + this.focusTarget.document.addEventListener("unload", this); + + // If we're not part of the active window, don't even start the timer yet. + let topWin = focusTarget.browsingContext.top.window; + if (topWin != Services.focus.activeWindow) { + return; + } + + this.startOnFocusDelay(); +}; + +EnableDelayHelper.prototype = { + get delayTime() { + return Services.prefs.getIntPref("security.dialog_enable_delay"); + }, + + handleEvent(event) { + if ( + !event.type.startsWith("key") && + event.target != this.focusTarget && + event.target != this.focusTarget.document + ) { + return; + } + + switch (event.type) { + case "keyup": + // As soon as any key goes up, we can stop treating keypresses + // as indicative of key-repeating that should prolong the timer. + this.focusTarget.removeEventListener("keyup", this, true); + this.focusTarget.removeEventListener("keydown", this, true); + break; + + case "keydown": + // Renew timer for repeating keydowns: + if (this._focusTimer) { + this._focusTimer.cancel(); + this._focusTimer = null; + this.startOnFocusDelay(); + event.preventDefault(); + } + break; + + case "blur": + this.onBlur(); + break; + + case "focus": + this.onFocus(); + break; + + case "unload": + this.onUnload(); + break; + } + }, + + onBlur() { + this.disableDialog(); + // If we blur while waiting to enable the buttons, just cancel the + // timer to ensure the delay doesn't fire while not focused. + if (this._focusTimer) { + this._focusTimer.cancel(); + this._focusTimer = null; + } + }, + + onFocus() { + this.startOnFocusDelay(); + }, + + onUnload() { + this.focusTarget.removeEventListener("blur", this); + this.focusTarget.removeEventListener("focus", this); + this.focusTarget.removeEventListener("keyup", this, true); + this.focusTarget.removeEventListener("keydown", this, true); + this.focusTarget.document.removeEventListener("unload", this); + + if (this._focusTimer) { + this._focusTimer.cancel(); + this._focusTimer = null; + } + + this.focusTarget = this.enableDialog = this.disableDialog = null; + }, + + startOnFocusDelay() { + if (this._focusTimer) { + return; + } + + this._focusTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + this._focusTimer.initWithCallback( + () => { + this.onFocusTimeout(); + }, + this.delayTime, + Ci.nsITimer.TYPE_ONE_SHOT + ); + }, + + onFocusTimeout() { + this._focusTimer = null; + this.enableDialog(); + }, +}; + +function makeSafe(fn) { + return function () { + // The dialog could be gone by now (if the user closed it), + // which makes it likely that the given fn might throw. + try { + fn(); + } catch (e) {} + }; +} -- cgit v1.2.3