Cuando completas el reto de escritura de ResistGate, consigues unos minutos en el sitio que intentabas abrir. Después, el bloqueo vuelve. En una extensión de Manifest V3, esa segunda parte es la difícil.
El worker con el que no puedes contar
El código en segundo plano de una extensión se ejecuta como un service worker, y Chrome lo detiene tras unos 30 segundos sin eventos. Todo lo que estaba en memoria desaparece, incluido un setTimeout que lleva la cuenta de tus 15 minutos.
chrome.alarms sobrevive al worker y lo vuelve a despertar. Pero la documentación de Chrome advierte que las alarmas pueden borrarse al reiniciar el navegador. Las reglas de sesión de declarativeNetRequest, que hacen las redirecciones, también se borran. Por eso, ni una alarma ni una regla pueden ser el único registro de que existe un permiso de acceso.
Guarda el vencimiento
Cuando consigues acceso, ResistGate escribe un registro en chrome.storage.local:
temporaryAccess[domain] = {
grantedAt: now,
expiresAt,
duration
};
expiresAt es una marca de tiempo. Todo lo demás se calcula a partir de ella.
Reconstruye todo desde el almacenamiento
Una función, updateBlockingRules, convierte ese registro en el comportamiento del navegador. Cada vez que se ejecuta, hace las mismas tres cosas:
- Elimina todos los registros de acceso cuyo
expiresAtya pasó. - Ajusta las alarmas a los registros restantes. Borra las alarmas de ResistGate que no tienen registro y crea una alarma en el
expiresAtde cada registro vigente. - Elimina todas las reglas de sesión y añade una regla de redirección por cada dominio bloqueado que no tenga acceso vigente.
Se ejecuta al arrancar el worker, al iniciar el navegador, cuando cambian los ajustes o los registros de acceso en el almacenamiento, y cuando se dispara una de sus alarmas. Todas las llamadas pasan por una misma cola de promesas para que dos actualizaciones nunca se solapen.
El manejador de la alarma no bloquea nada por su cuenta. Pide la misma actualización que los demás eventos. Si el worker estaba dormido cuando venció tu permiso, la siguiente ejecución encuentra un expiresAt en el pasado y restablece la regla de redirección. Si un reinicio borró las alarmas y las reglas, el arranque reconstruye ambas desde el almacenamiento.
Una prueba de Vitest entrega al paso de limpieza un registro vigente y uno vencido, y comprueba que solo quede el vigente. Sigo comprobando el flujo real a mano en Chrome: bloqueo un sitio, consigo acceso, espero y verifico que vuelva a bloquearse.
El hueco que queda
El almacenamiento puede decirle a un worker que acaba de despertar que un permiso venció. No puede despertarlo. Si Chrome perdiera una alarma y no ocurriera ningún otro evento, el sitio seguiría abierto hasta el siguiente evento. El inicio del navegador es el caso sobre el que advierte la documentación, y es precisamente cuando se reconstruyen las alarmas.
Lo que me llevaría a cualquier código basado en eventos: guarda el estado que necesitas para recuperarte, encarga a una sola función convertirlo en efectos y deja que cada evento le pida a esa función que vuelva a ejecutarse.
El código está en GitHub si quieres leer updateBlockingRules y syncExpiryAlarms en background.js.
Un pequeño voto de confianza
¿Valió la pena leer esta nota?