Skip to content

Commit

Permalink
fix: implement keep-alive check for Firefox alarms for #8
Browse files Browse the repository at this point in the history
  • Loading branch information
KarimAziev committed Aug 21, 2024
1 parent 8df6423 commit fbbc73d
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 59 deletions.
3 changes: 2 additions & 1 deletion firefox/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
"activeTab",
"scripting",
"storage",
"tabs"
"tabs",
"alarms"
],
"host_permissions": [
"http://*/*",
Expand Down
61 changes: 3 additions & 58 deletions src/content-script-tools/text-syncer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,51 +17,7 @@ const errorMessageByCode: {
1006: `Failed to connect to server: <i>${WS_URL}</i>`,
};

// 5 seconds
const KEEP_ALIVE_INTERVAL = 5000;

const currentBrowser = process.env.BROWSER_TARGET;
const shouldStartKeepAlive = currentBrowser === 'firefox';

class TextSyncer {
private keepAliveIntervalId: NodeJS.Timeout | null = null;
/**
* Starts the keep-alive procedure to maintain communication with the background script.
*
* This mechanism is specifically implemented because Firefox may terminate the service worker
* and close the WebSocket connection if no events occur for 30 seconds, thereby disconnecting you.
*
* This mess is needed in Firefox due to their lack of support for keeping service workers alive with
* WebSocket activity alone (unlike Chrome from version 116 onward).
*
* @param port The Chrome Extension's port for background communication.
*/
private startKeepAliveLoop(port: chrome.runtime.Port): void {
if (this.keepAliveIntervalId) {
clearTimeout(this.keepAliveIntervalId);
}

this.keepAliveIntervalId = setTimeout(() => {
try {
this.post(port, 'keepalive');
this.startKeepAliveLoop(port);
} catch (error) {
console.error('Failed to send keepalive message:', error);
this.stopKeepAlive();
}
}, KEEP_ALIVE_INTERVAL);
}

/**
* Stops the keep-alive procedure, ensuring that no further keep-alive messages are sent.
*/
private stopKeepAlive(): void {
if (this.keepAliveIntervalId) {
clearInterval(this.keepAliveIntervalId);
this.keepAliveIntervalId = null;
}
}

/**
* Establishes a connection between the content script and background script,
* and sets up listeners for text updates and disconnect events.
Expand All @@ -80,17 +36,10 @@ class TextSyncer {

this.register(port, url, title, handler, options);

port.onMessage.addListener(this.makeMessageListener(handler, port));

port.onMessage.addListener(this.makeMessageListener(handler));
const textChangeListener = this.makeTextChangeListener(port, handler);

if (shouldStartKeepAlive) {
this.startKeepAliveLoop(port);
}

handler.bindChange(textChangeListener, false);
port.onDisconnect.addListener(() => {
this.stopKeepAlive();
handler.unbindChange(textChangeListener, false);
});
}
Expand All @@ -101,12 +50,8 @@ class TextSyncer {
* @param handler - The handler instance managing the text element.
* @returns A function to be used as the onMessage event listener.
*/
private makeMessageListener(handler: IHandler, port: chrome.runtime.Port) {
private makeMessageListener(handler: IHandler) {
return (msg: MessageEventData) => {
if (shouldStartKeepAlive) {
this.startKeepAliveLoop(port);
}

if (this[msg.type]) {
return this[msg.type](handler, msg.payload);
}
Expand Down Expand Up @@ -209,7 +154,7 @@ class TextSyncer {
private post<T extends keyof SocketPostPayloadMap>(
port: chrome.runtime.Port,
type: T,
payload?: SocketPostPayloadMap[T],
payload: SocketPostPayloadMap[T],
) {
return port.postMessage({ type, payload });
}
Expand Down
18 changes: 18 additions & 0 deletions src/service-worker.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
import { wsBridge } from '@/background-tools';

const currentBrowser = process.env.BROWSER_TARGET;
const isFirefox = currentBrowser === 'firefox';

if (isFirefox) {
chrome.alarms.create('keepAliveAlarm', { periodInMinutes: 0.1 });

chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'keepAliveAlarm') {
// Perform a lightweight task to keep the service worker alive
chrome.runtime.getPlatformInfo((info) => {
console.log(
`Alarm triggered: keeping service worker alive. Platform info: ${info.os}`,
);
});
}
});
}

/**
* Adds an event listener to the Chrome extension's action button (e.g., toolbar icon).
* On click, it injects the 'content-script.js' into the current tab.
Expand Down

0 comments on commit fbbc73d

Please sign in to comment.