summaryrefslogtreecommitdiffstats
path: root/toolkit/components/prompts/src/PromptUtils.sys.mjs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
commit9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /toolkit/components/prompts/src/PromptUtils.sys.mjs
parentInitial commit. (diff)
downloadthunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.tar.xz
thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/prompts/src/PromptUtils.sys.mjs')
-rw-r--r--toolkit/components/prompts/src/PromptUtils.sys.mjs186
1 files changed, 186 insertions, 0 deletions
diff --git a/toolkit/components/prompts/src/PromptUtils.sys.mjs b/toolkit/components/prompts/src/PromptUtils.sys.mjs
new file mode 100644
index 0000000000..7a15802b84
--- /dev/null
+++ b/toolkit/components/prompts/src/PromptUtils.sys.mjs
@@ -0,0 +1,186 @@
+/* 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);
+
+ 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) {}
+ };
+}