summaryrefslogtreecommitdiffstats
path: root/comm/calendar/base/src/CalMetronome.jsm
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /comm/calendar/base/src/CalMetronome.jsm
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.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 'comm/calendar/base/src/CalMetronome.jsm')
-rw-r--r--comm/calendar/base/src/CalMetronome.jsm142
1 files changed, 142 insertions, 0 deletions
diff --git a/comm/calendar/base/src/CalMetronome.jsm b/comm/calendar/base/src/CalMetronome.jsm
new file mode 100644
index 0000000000..b675e5bc0a
--- /dev/null
+++ b/comm/calendar/base/src/CalMetronome.jsm
@@ -0,0 +1,142 @@
+/* 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 EXPORTED_SYMBOLS = ["CalMetronome"];
+
+const { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+const { EventEmitter } = ChromeUtils.importESModule("resource://gre/modules/EventEmitter.sys.mjs");
+
+const MINUTE_IN_MS = 60000;
+const HOUR_IN_MS = 3600000;
+const DAY_IN_MS = 86400000;
+
+/**
+ * Keeps calendar UI/components in sync by ticking regularly. Fires a "minute"
+ * event every minute on the minute, an "hour" event on the hour, and a "day"
+ * event at midnight. Each event also fires if longer than the time period in
+ * question has elapsed since the last event, e.g. because the computer has
+ * been asleep.
+ *
+ * It automatically corrects clock skew: if a minute event is more than one
+ * second late, the time to the next event is recalculated and should fire a
+ * few milliseconds late at worst.
+ *
+ * @implements nsIObserver
+ * @implements EventEmitter
+ */
+var CalMetronome = {
+ QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
+
+ /**
+ * The time when the minute event last fired, in milliseconds since the epoch.
+ *
+ * @type integer
+ */
+ _lastFireTime: 0,
+
+ /**
+ * The last minute for which the minute event fired.
+ *
+ * @type integer (0-59)
+ */
+ _lastMinute: -1,
+
+ /**
+ * The last hour for which the hour event fired.
+ *
+ * @type integer (0-23)
+ */
+ _lastHour: -1,
+
+ /**
+ * The last day of the week for which the day event fired.
+ *
+ * @type integer (0-7)
+ */
+ _lastDay: -1,
+
+ /**
+ * The timer running everything.
+ *
+ * @type nsITimer
+ */
+ _timer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
+
+ init() {
+ let now = new Date();
+ this._lastFireTime = now.valueOf();
+ this._lastHour = now.getHours();
+ this._lastDay = now.getDay();
+
+ EventEmitter.decorate(this);
+
+ Services.obs.addObserver(this, "wake_notification");
+ Services.obs.addObserver(this, "quit-application");
+ this._startNext();
+ },
+
+ observe(subject, topic, data) {
+ if (topic == "wake_notification") {
+ cal.LOGverbose("[CalMetronome] Observed wake_notification");
+ this.notify();
+ } else if (topic == "quit-application") {
+ this._timer.cancel();
+ Services.obs.removeObserver(this, "wake_notification");
+ Services.obs.removeObserver(this, "quit-application");
+ }
+ },
+
+ _startNext() {
+ this._timer.cancel();
+
+ let now = new Date();
+ let next = new Date(
+ now.getFullYear(),
+ now.getMonth(),
+ now.getDate(),
+ now.getHours(),
+ now.getMinutes() + 1,
+ 0
+ );
+ cal.LOGverbose(`[CalMetronome] Scheduling one-off event in ${next - now}ms`);
+ this._timer.initWithCallback(this, next - now, Ci.nsITimer.TYPE_ONE_SHOT);
+ },
+
+ _startRepeating() {
+ cal.LOGverbose(`[CalMetronome] Starting repeating events`);
+ this._timer.initWithCallback(this, MINUTE_IN_MS, Ci.nsITimer.TYPE_REPEATING_SLACK);
+ },
+
+ notify() {
+ let now = new Date();
+ let elapsedSinceLastFire = now.valueOf() - this._lastFireTime;
+ this._lastFireTime = now.valueOf();
+
+ let minute = now.getMinutes();
+ if (minute != this._lastMinute || elapsedSinceLastFire > MINUTE_IN_MS) {
+ this._lastMinute = minute;
+ this.emit("minute", now);
+ }
+
+ let hour = now.getHours();
+ if (hour != this._lastHour || elapsedSinceLastFire > HOUR_IN_MS) {
+ this._lastHour = hour;
+ this.emit("hour", now);
+ }
+
+ let day = now.getDay();
+ if (day != this._lastDay || elapsedSinceLastFire > DAY_IN_MS) {
+ this._lastDay = day;
+ this.emit("day", now);
+ }
+
+ let slack = now.getSeconds();
+ if (slack >= 1 && slack < 59) {
+ this._startNext();
+ } else if (this._timer.type == Ci.nsITimer.TYPE_ONE_SHOT) {
+ this._startRepeating();
+ }
+ },
+};
+CalMetronome.init();