Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions extension/js/common/browser/browser-msg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,14 +452,16 @@ export class BrowserMsg {
public static async createIntervalAlarm(action: string, periodInMinutes: number) {
const alarmName = `${action}_interval`;

await chrome.alarms.create(alarmName, { periodInMinutes });
if (!(await chrome.alarms.get(alarmName))) {
await chrome.alarms.create(alarmName, { periodInMinutes });
}
}

public static intervalAddListener(name: string, handler: IntervalHandler) {
BrowserMsg.INTERVAL_HANDLERS[name] = handler;
}

public static bgListen() {
public static bgListen(ready: Promise<void>) {
chrome.runtime.onMessage.addListener((msg: Bm.Raw, _sender, rawRespond: (rawRes: Bm.RawResponse) => void) => {
const respondIfPageStillOpen = (response: Bm.RawResponse) => {
try {
Expand All @@ -480,7 +482,10 @@ export class BrowserMsg {
if (Object.keys(BrowserMsg.HANDLERS_REGISTERED_BACKGROUND).includes(msg.name)) {
// standard or broadcast message
const handler: Bm.AsyncRespondingHandler = BrowserMsg.HANDLERS_REGISTERED_BACKGROUND[msg.name];
BrowserMsg.sendRawResponse(handler(msg.data.bm), respondIfPageStillOpen);
BrowserMsg.sendRawResponse(
ready.then(() => handler(msg.data.bm)),
respondIfPageStillOpen
);
return true; // will respond
} else {
// broadcast message that backend does not have a handler for - ignored
Expand Down
94 changes: 61 additions & 33 deletions extension/js/service_worker/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import { GoogleOAuth } from '../common/api/authentication/google/google-oauth.js';
import { Bm, BrowserMsg } from '../common/browser/browser-msg.js';
import { storageGet, storageSet } from '../common/browser/chrome.js';
import { emailKeyIndex } from '../common/core/common.js';
import { ExpirationCache } from '../common/core/expiration-cache.js';
import { BgHandlers } from './bg-handlers.js';
Expand All @@ -25,16 +26,16 @@ import { ConfiguredIdpOAuth } from '../common/api/authentication/configured-idp-

console.info('background.js service worker starting');

(async () => {
let db: IDBDatabase;
let db: IDBDatabase;
const inMemoryStore = new ExpirationCache<string>('in_memory_store', 4 * 60 * 60 * 1000); // 4 hours

// Start initialization after all event listeners below have been registered synchronously.
const ready = Promise.resolve().then(async () => {
let storage: GlobalStoreDict;
const inMemoryStore = new ExpirationCache<string>('in_memory_store', 4 * 60 * 60 * 1000); // 4 hours
await chrome.alarms.clearAll();
await BrowserMsg.createIntervalAlarm('delete_expired', 1); // each minute

try {
await migrateGlobal();
await GlobalStore.set({ version: Number(VERSION.replace(/\./g, '')) });
storage = await GlobalStore.get(['settings_seen']);
} catch (e) {
await BgUtils.handleStoreErr(GlobalStore.errCategorize(e));
Expand All @@ -56,34 +57,61 @@ console.info('background.js service worker starting');
await BgUtils.handleStoreErr(e);
return;
}
// storage related handlers
});

BrowserMsg.bgAddListener('db', (r: Bm.Db) => BgHandlers.dbOperationHandler(db, r));
BrowserMsg.bgAddListener('inMemoryStoreSet', async (r: Bm.InMemoryStoreSet) => inMemoryStore.set(emailKeyIndex(r.acctEmail, r.key), r.value, r.expiration));
BrowserMsg.bgAddListener('inMemoryStoreGet', async (r: Bm.InMemoryStoreGet) => inMemoryStore.get(emailKeyIndex(r.acctEmail, r.key)));
BrowserMsg.bgAddListener('db', (r: Bm.Db) => BgHandlers.dbOperationHandler(db, r));
BrowserMsg.bgAddListener('inMemoryStoreSet', async (r: Bm.InMemoryStoreSet) => inMemoryStore.set(emailKeyIndex(r.acctEmail, r.key), r.value, r.expiration));
BrowserMsg.bgAddListener('inMemoryStoreGet', async (r: Bm.InMemoryStoreGet) => inMemoryStore.get(emailKeyIndex(r.acctEmail, r.key)));

BrowserMsg.bgAddListener('ajax', BgHandlers.ajaxHandler);
BrowserMsg.bgAddListener('ajaxGmailAttachmentGetChunk', BgHandlers.ajaxGmailAttachmentGetChunkHandler);
BrowserMsg.bgAddListener('expirationCacheGet', BgHandlers.expirationCacheGetHandler);
BrowserMsg.bgAddListener('expirationCacheSet', BgHandlers.expirationCacheSetHandler);
BrowserMsg.bgAddListener('expirationCacheDeleteExpired', BgHandlers.expirationCacheDeleteExpiredHandler);
BrowserMsg.bgAddListener('getApiAuthorization', BgHandlers.getApiAuthorization);
BrowserMsg.bgAddListener('settings', BgHandlers.openSettingsPageHandler);
BrowserMsg.bgAddListener('update_uninstall_url', BgHandlers.updateUninstallUrl);
BrowserMsg.bgAddListener('get_active_tab_info', BgHandlers.getActiveTabInfo);
BrowserMsg.bgAddListener('reconnect_acct_auth_popup', (r: Bm.ReconnectAcctAuthPopup) => GoogleOAuth.newAuthPopup(r));
BrowserMsg.bgAddListener('reconnect_custom_idp_acct_auth_popup', (r: Bm.ReconnectCustomIDPAcctAuthPopup) => ConfiguredIdpOAuth.newAuthPopup(r.acctEmail));
BrowserMsg.intervalAddListener('delete_expired', inMemoryStore.deleteExpired);
BrowserMsg.bgListen();
BrowserMsg.alarmListen();
await BgHandlers.updateUninstallUrl({});
injectFcIntoWebmail();
BrowserMsg.bgAddListener('ajax', BgHandlers.ajaxHandler);
BrowserMsg.bgAddListener('ajaxGmailAttachmentGetChunk', BgHandlers.ajaxGmailAttachmentGetChunkHandler);
BrowserMsg.bgAddListener('expirationCacheGet', BgHandlers.expirationCacheGetHandler);
BrowserMsg.bgAddListener('expirationCacheSet', BgHandlers.expirationCacheSetHandler);
BrowserMsg.bgAddListener('expirationCacheDeleteExpired', BgHandlers.expirationCacheDeleteExpiredHandler);
BrowserMsg.bgAddListener('getApiAuthorization', BgHandlers.getApiAuthorization);
BrowserMsg.bgAddListener('settings', BgHandlers.openSettingsPageHandler);
BrowserMsg.bgAddListener('update_uninstall_url', BgHandlers.updateUninstallUrl);
BrowserMsg.bgAddListener('get_active_tab_info', BgHandlers.getActiveTabInfo);
BrowserMsg.bgAddListener('reconnect_acct_auth_popup', (r: Bm.ReconnectAcctAuthPopup) => GoogleOAuth.newAuthPopup(r));
BrowserMsg.bgAddListener('reconnect_custom_idp_acct_auth_popup', (r: Bm.ReconnectCustomIDPAcctAuthPopup) => ConfiguredIdpOAuth.newAuthPopup(r.acctEmail));
BrowserMsg.intervalAddListener('delete_expired', async () => {
await ready;
return inMemoryStore.deleteExpired();
});

if (Catch.isThunderbirdMail()) {
BgHandlers.thunderbirdSecureComposeHandler();
await BgHandlers.thunderbirdContentScriptRegistration();
BrowserMsg.bgAddListener('thunderbirdGetCurrentUser', BgHandlers.thunderbirdGetCurrentUserHandler);
BrowserMsg.bgAddListener('thunderbirdMsgGet', BgHandlers.thunderbirdMsgGetHandler);
BrowserMsg.bgAddListener('thunderbirdOpenPassphraseDialog', BgHandlers.thunderbirdOpenPassphraseDialog);
}
})().catch(Catch.reportErr);
if (Catch.isThunderbirdMail()) {
BgHandlers.thunderbirdSecureComposeHandler(ready);
BrowserMsg.bgAddListener('thunderbirdGetCurrentUser', BgHandlers.thunderbirdGetCurrentUserHandler);
BrowserMsg.bgAddListener('thunderbirdMsgGet', BgHandlers.thunderbirdMsgGetHandler);
BrowserMsg.bgAddListener('thunderbirdOpenPassphraseDialog', BgHandlers.thunderbirdOpenPassphraseDialog);
}

BrowserMsg.bgListen(ready);
BrowserMsg.alarmListen();

chrome.runtime.onInstalled.addListener(() => {
void ready
.then(async () => {
await GlobalStore.set({ version: Number(VERSION.replace(/\./g, '')) });
await BgHandlers.updateUninstallUrl({});
})
.catch(Catch.reportErr);
});

chrome.runtime.onStartup.addListener(() => {
// Wake the worker on browser startup so the per-session initialization below runs.
});

void ready
.then(async () => {
// Session storage survives worker restarts, but is cleared on disable, reload, update, and browser restart.
const session = await storageGet('session', ['webmailInjectionStarted']);
if (!session.webmailInjectionStarted) {
injectFcIntoWebmail();
await storageSet('session', { webmailInjectionStarted: true });
}
if (Catch.isThunderbirdMail()) {
await BgHandlers.thunderbirdContentScriptRegistration();
}
})
.catch(Catch.reportErr);
4 changes: 3 additions & 1 deletion extension/js/service_worker/bg-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export class BgHandlers {
});
});

public static thunderbirdSecureComposeHandler = () => {
public static thunderbirdSecureComposeHandler = (ready: Promise<void>) => {
const handleClickEvent = async (tabId: number, acctEmail: string, thunderbirdMsgId: number, composeMethod?: messenger.compose._ComposeDetailsType) => {
const accountEmails = await GlobalStore.acctEmailsGet();
const useFullScreenSecureCompose = (await messenger.windows.getCurrent()).type === 'messageCompose';
Expand All @@ -132,13 +132,15 @@ export class BgHandlers {
}
};
messenger.composeAction.onClicked.addListener(async tab => {
await ready;
const messageDetails = await messenger.compose.getComposeDetails(Number(tab.id));
const composeMethod = messageDetails.type;
const msgId = Number(messageDetails.relatedMessageId);
const acctEmail = Str.parseEmail(messageDetails.from as string).email;
if (acctEmail) await handleClickEvent(Number(tab.id), acctEmail, msgId, composeMethod);
});
messenger.messageDisplayAction.onClicked.addListener(async tab => {
await ready;
const tabId = Number(tab.id);
const messageDetails = await messenger.messageDisplay.getDisplayedMessage(tabId);
if (messageDetails) {
Expand Down
13 changes: 2 additions & 11 deletions extension/js/service_worker/inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,12 @@ const isContentScriptInjectionNeeded = (tabId: number, callback: (injected: bool
};

const injectContentScripts = (tabId: number, files: string[], callback?: () => void) => {
const filesCopy = files.slice();
const scriptFile = filesCopy.shift();

chrome.scripting.executeScript(
{
target: { tabId },
files: scriptFile ? [scriptFile] : [],
files,
injectImmediately: true,
},
() => {
if (filesCopy.length) {
injectContentScripts(tabId, filesCopy, callback);
} else if (callback) {
callback();
}
}
() => callback?.()
);
};
2 changes: 1 addition & 1 deletion extension/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
"matches": ["https://mail.google.com/*", "https://accounts.google.com/*", "https://www.google.com/*"]
}
],
"minimum_chrome_version": "96",
"minimum_chrome_version": "106",
"content_security_policy": {
"extension_pages": "script-src 'self'; default-src 'self'; frame-ancestors 'self' https://mail.google.com; img-src 'self' https://* data: blob:; frame-src 'self' blob:; worker-src 'self'; form-action 'none'; media-src 'none'; font-src 'none'; manifest-src 'none'; object-src 'none'; base-uri 'self'; connect-src 'self' *; style-src 'self' 'unsafe-inline';"
}
Expand Down
Loading