0

I'm making a Firefox browser extension (note: Chrome extensions and Firefox extensions have very similar APIs) that monitors screentime on certain websites that can be selected by the user. Specifically, the screen time counter should reset every 24 hours at midnight.

The code in question:

let interval = setInterval(update, 1000);

let msTillMidnight = (new Date().setHours(24, 0, 0, 0) - Date.now());

function restart() {
  browser.storage.local.get("totalTime").then(res => {
    browser.storage.local.set({timeLeft: res["totalTime"]});
  });
  if (!running) {
    running = true;
    interval = setInterval(update, 1000);
  }
}

new Promise(resolve => setTimeout(resolve, msTillMidnight)).then(() => { // I am aware this is laughably redundant, but in theory it should work exactly the same
  restart();
  setInterval(restart, 1000 * 60 * 60 * 24); // 1000 ms/second —> 60 s/minute —> 60 m/hour —> 24 h/day
});

I noticed a very odd behavior. After the first midnight after the extension began running, restart was not called. None of the browser storage vars were updated, nor the interval, not the running bool. The problem, I assumed, was that the setTimeout in question was simply not running. However, the next night, the clock had reset!

I don't understand. Is something wrong with my msTillMidnight variable? Is it because the background page shuts down and won't run any setTimeout scripts after a while when my computer is asleep at midnight and that the restart function will only call when my computer is still on or shut down recently? Or maybe, my redundant line of code that beats around the bush instead of a simple setTimeout is somehow causing a bug?

I would love to debug this on my own, but my testing cycles would most likely be 24 hours, which is a little slower than I would prefer...

E_net4
  • 27,810
  • 13
  • 101
  • 139
gosoccerboy5
  • 115
  • 2
  • 9
  • I don't see any calls to `clearInterval()` – Pointy Nov 08 '22 at 01:04
  • 1
    If your computer goes to sleep, the timer that fires setTimeout will not advance during that time. For example if it's 2 hours until midnight and computer went to sleep for 1 hour, when it wake up, the timer will fire after 2 hours. It sounds like your best option is do the date check inside `update` function. Or have another `setInterval` that check current date every second or so. – vanowm Nov 08 '22 at 01:45
  • @Pointy There's a clearInterval, just outside of the snippet – gosoccerboy5 Nov 08 '22 at 04:25
  • The design could be improved by using an [exponentially weighted moving average](https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average) and by giving more weight to times when a user is actively using the page. Clearly it's not "screen time" when the timer has gone dormant for lack of activity. – Yogi Nov 08 '22 at 09:02

2 Answers2

1

setTimeout and setInterval don't rely on computer's clock. They are timers, so if browser "lost" some time due to computer sleep, the timers will not advance during that time.

You'll need use setInterval and check date every nn seconds. This however might also fail if extension was suspended.

A be better approach might be to use alarms instead. Specifically, by using when in alarmInfo option. However alarms will "catch up" if missed (for example in your case if computer was sleeping for 7 days, when woke up, it will trigger 7 alarms at once)

This should work fine with manifest v2, however manifest v3 doesn't have background processes and it suspends extensions after 15 minutes or so, even alarms won't fire. There are a few work arounds this, none of which is perfect.

vanowm
  • 9,466
  • 2
  • 21
  • 37
  • That seems good enough. I can probably keep the background interval running constantly and have a check inside the interval instead of turning it on and off. Thanks – gosoccerboy5 Nov 08 '22 at 04:41
  • Well, it seems that the alarms API didn't work, but I just made an in-house implementation that hopefully will work. – gosoccerboy5 Nov 08 '22 at 21:24
  • Perhaps maybe you could share your implementation with the rest of the world in an answer? ;) – vanowm Nov 09 '22 at 00:16
  • Good idea, although my solution might not be considered best practices. lol (and i'm still testing it) – gosoccerboy5 Nov 09 '22 at 13:15
0

Ok, if anyone from the future is viewing this. Yes, I fixed it. More specifically, vanowm's answer worked for me.

Although, for some reason the native alarm implementation wasn't working, so I made my own that still works.

gosoccerboy5
  • 115
  • 2
  • 9