Skip to content

Commit 3b16204

Browse files
tomholubIoan Moldovan
authored andcommitted
issue #5747 refactor content scripts for Thunderbird (#5748)
* issue 5747 initial refactor * removed dated comment * cleanup * added a generic folder * remove unused notifyMurdered parameter * added comment * upload debug artifacts * remove verbose google-mock listening logs
1 parent 0b627aa commit 3b16204

File tree

9 files changed

+273
-245
lines changed

9 files changed

+273
-245
lines changed

.semaphore/semaphore.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
version: v1.0
2-
name: Flowcrypt Node Core Tests
2+
name: FlowCrypt Extension Tests
33
agent:
44
machine:
55
type: e2-standard-4

extension/js/content_scripts/webmail/setup-webmail-content-script.ts renamed to extension/js/content_scripts/webmail/generic/setup-webmail-content-script.ts

Lines changed: 50 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,37 @@
33
'use strict';
44

55
import Swal from 'sweetalert2';
6-
import { AccountServer } from '../../common/api/account-server.js';
7-
import { KeyManager } from '../../common/api/key-server/key-manager.js';
8-
import { ApiErr, BackendAuthErr } from '../../common/api/shared/api-error.js';
9-
import { BrowserMsgCommonHandlers } from '../../common/browser/browser-msg-common-handlers.js';
10-
import { Bm, BrowserMsg, TabIdRequiredError } from '../../common/browser/browser-msg.js';
11-
import { ContentScriptWindow } from '../../common/browser/browser-window.js';
12-
import { Env, WebMailName } from '../../common/browser/env.js';
13-
import { Time } from '../../common/browser/time.js';
14-
import { CommonHandlers, Ui } from '../../common/browser/ui.js';
15-
import { ClientConfiguration, ClientConfigurationError } from '../../common/client-configuration.js';
16-
import { Str, Url } from '../../common/core/common.js';
17-
import { InMemoryStoreKeys, VERSION } from '../../common/core/const.js';
18-
import { getLocalKeyExpiration, processAndStoreKeysFromEkmLocally } from '../../common/helpers.js';
19-
import { Injector } from '../../common/inject.js';
20-
import { Lang } from '../../common/lang.js';
21-
import { Notifications } from '../../common/notifications.js';
22-
import { Catch } from '../../common/platform/catch.js';
23-
import { AcctStore } from '../../common/platform/store/acct-store.js';
24-
import { GlobalStore } from '../../common/platform/store/global-store.js';
25-
import { InMemoryStore } from '../../common/platform/store/in-memory-store.js';
26-
import { WebmailVariantString, XssSafeFactory } from '../../common/xss-safe-factory.js';
27-
import { RelayManager } from '../../common/relay-manager.js';
6+
import { AccountServer } from '../../../common/api/account-server.js';
7+
import { KeyManager } from '../../../common/api/key-server/key-manager.js';
8+
import { ApiErr, BackendAuthErr } from '../../../common/api/shared/api-error.js';
9+
import { BrowserMsgCommonHandlers } from '../../../common/browser/browser-msg-common-handlers.js';
10+
import { Bm, BrowserMsg, TabIdRequiredError } from '../../../common/browser/browser-msg.js';
11+
import { ContentScriptWindow } from '../../../common/browser/browser-window.js';
12+
import { Env, WebMailName } from '../../../common/browser/env.js';
13+
import { Time } from '../../../common/browser/time.js';
14+
import { CommonHandlers, Ui } from '../../../common/browser/ui.js';
15+
import { ClientConfiguration, ClientConfigurationError } from '../../../common/client-configuration.js';
16+
import { Str, Url } from '../../../common/core/common.js';
17+
import { InMemoryStoreKeys, VERSION } from '../../../common/core/const.js';
18+
import { getLocalKeyExpiration, processAndStoreKeysFromEkmLocally } from '../../../common/helpers.js';
19+
import { Injector } from '../../../common/inject.js';
20+
import { Lang } from '../../../common/lang.js';
21+
import { Notifications } from '../../../common/notifications.js';
22+
import { Catch } from '../../../common/platform/catch.js';
23+
import { AcctStore } from '../../../common/platform/store/acct-store.js';
24+
import { GlobalStore } from '../../../common/platform/store/global-store.js';
25+
import { InMemoryStore } from '../../../common/platform/store/in-memory-store.js';
26+
import { WebmailVariantString, XssSafeFactory } from '../../../common/xss-safe-factory.js';
27+
import { RelayManager } from '../../../common/relay-manager.js';
28+
import { WebmailElementReplacer } from './webmail-element-replacer.js';
2829

2930
export type WebmailVariantObject = {
3031
newDataLayer: undefined | boolean;
3132
newUi: undefined | boolean;
3233
email: undefined | string;
3334
gmailVariant: WebmailVariantString;
3435
};
35-
export type IntervalFunction = { interval: number; handler: () => void };
36+
3637
type WebmailSpecificInfo = {
3738
name: WebMailName;
3839
variant: WebmailVariantString;
@@ -45,17 +46,9 @@ type WebmailSpecificInfo = {
4546
inject: Injector,
4647
notifications: Notifications,
4748
factory: XssSafeFactory,
48-
notifyMurdered: () => void,
4949
relayManager: RelayManager
5050
) => Promise<void>;
5151
};
52-
export interface WebmailElementReplacer {
53-
getIntervalFunctions: () => IntervalFunction[];
54-
setReplyBoxEditable: () => Promise<void>;
55-
reinsertReplyBox: (replyMsgId: string) => void;
56-
scrollToReplyBox: (replyMsgId: string) => void;
57-
scrollToCursorInReplyBox: (replyMsgId: string, cursorOffsetTop: number) => void;
58-
}
5952

6053
const win = window as unknown as ContentScriptWindow;
6154

@@ -275,23 +268,6 @@ export const contentScriptSetupIfVacant = async (webmailSpecific: WebmailSpecifi
275268
}
276269
};
277270

278-
const notifyMurdered = () => {
279-
const notifEl = document.getElementsByClassName('webmail_notifications')[0];
280-
const div = document.createElement('div');
281-
div.innerText = 'FlowCrypt has updated, please reload the tab. ';
282-
div.classList.add('webmail_notification');
283-
const a = document.createElement('a');
284-
a.href = '#';
285-
a.onclick = function () {
286-
const parent = (this as HTMLAnchorElement).parentNode as HTMLElement | undefined;
287-
parent?.remove();
288-
};
289-
a.textContent = 'close';
290-
div.appendChild(a);
291-
notifEl.textContent = '';
292-
notifEl.appendChild(div);
293-
};
294-
295271
const showPassphraseDialog = async (factory: XssSafeFactory, { longids, type, initiatorFrameId }: Bm.PassphraseDialog) => {
296272
await factory.showPassphraseDialog(longids, type, initiatorFrameId);
297273
};
@@ -451,7 +427,7 @@ export const contentScriptSetupIfVacant = async (webmailSpecific: WebmailSpecifi
451427
ppEvent,
452428
Catch.try(() => notifyExpiringKeys(acctEmail, clientConfiguration, notifications))
453429
);
454-
await webmailSpecific.start(acctEmail, clientConfiguration, inject, notifications, factory, notifyMurdered, relayManager);
430+
await webmailSpecific.start(acctEmail, clientConfiguration, inject, notifications, factory, relayManager);
455431
} catch (e) {
456432
if (e instanceof TabIdRequiredError) {
457433
console.error(`FlowCrypt cannot start: ${String(e)}`);
@@ -527,3 +503,28 @@ export const contentScriptSetupIfVacant = async (webmailSpecific: WebmailSpecifi
527503
}
528504
}
529505
};
506+
507+
/**
508+
* This happens when Firefox (or possibly Thunderbird) just updated FlowCrypt.
509+
*
510+
* Previous (meaning this currently running) instance of FlowCrypt will no longer
511+
* have access to its various classes or global variables, and is left in a
512+
* semi-functioning state. The best we can do is to ask the user to reload
513+
* the tab, which will load the newly updated version of the extension cleanly.
514+
*/
515+
export const notifyMurdered = () => {
516+
const notifEl = document.getElementsByClassName('webmail_notifications')[0];
517+
const div = document.createElement('div');
518+
div.innerText = 'FlowCrypt has updated, please reload the tab. ';
519+
div.classList.add('webmail_notification');
520+
const a = document.createElement('a');
521+
a.href = '#';
522+
a.onclick = function () {
523+
const parent = (this as HTMLAnchorElement).parentNode as HTMLElement | undefined;
524+
parent?.remove();
525+
};
526+
a.textContent = 'close';
527+
div.appendChild(a);
528+
notifEl.textContent = '';
529+
notifEl.appendChild(div);
530+
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/* ©️ 2016 - present FlowCrypt a.s. Limitations apply. Contact [email protected] */
2+
3+
import { ContentScriptWindow } from '../../../common/browser/browser-window';
4+
import { notifyMurdered } from './setup-webmail-content-script';
5+
6+
export type IntervalFunction = { interval: number; handler: () => void };
7+
8+
export abstract class WebmailElementReplacer {
9+
private replacePgpElsInterval: number;
10+
11+
public abstract getIntervalFunctions: () => IntervalFunction[];
12+
public abstract setReplyBoxEditable: () => Promise<void>;
13+
public abstract reinsertReplyBox: (replyMsgId: string) => void;
14+
public abstract scrollToReplyBox: (replyMsgId: string) => void;
15+
public abstract scrollToCursorInReplyBox: (replyMsgId: string, cursorOffsetTop: number) => void;
16+
17+
public runIntervalFunctionsPeriodically = () => {
18+
const intervalFunctions = this.getIntervalFunctions();
19+
for (const intervalFunction of intervalFunctions) {
20+
intervalFunction.handler();
21+
this.replacePgpElsInterval = (window as unknown as ContentScriptWindow).TrySetDestroyableInterval(() => {
22+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
23+
if (typeof (window as any).$ === 'function') {
24+
intervalFunction.handler();
25+
} else {
26+
// firefox will unload jquery when extension is restarted or updated
27+
clearInterval(this.replacePgpElsInterval);
28+
notifyMurdered();
29+
}
30+
}, intervalFunction.interval);
31+
}
32+
};
33+
}

extension/js/content_scripts/webmail/gmail-element-replacer.ts renamed to extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,34 @@
22

33
'use strict';
44

5-
import { Dict, Str } from '../../common/core/common.js';
6-
import { FactoryReplyParams, XssSafeFactory } from '../../common/xss-safe-factory.js';
7-
import { IntervalFunction, WebmailElementReplacer } from './setup-webmail-content-script.js';
8-
import { ApiErr } from '../../common/api/shared/api-error.js';
9-
import { Attachment } from '../../common/core/attachment.js';
10-
import { BrowserMsg } from '../../common/browser/browser-msg.js';
11-
import { Catch } from '../../common/platform/catch.js';
12-
import { GlobalStore, LocalDraft } from '../../common/platform/store/global-store.js';
13-
import { Injector } from '../../common/inject.js';
14-
import { PubLookup } from '../../common/api/pub-lookup.js';
15-
import { Notifications } from '../../common/notifications.js';
16-
import { PgpArmor } from '../../common/core/crypto/pgp/pgp-armor.js';
17-
import { Ui } from '../../common/browser/ui.js';
18-
import { WebmailCommon } from '../../common/webmail.js';
19-
import { Xss } from '../../common/platform/xss.js';
20-
import { ClientConfiguration } from '../../common/client-configuration.js';
5+
import { Dict, Str } from '../../../common/core/common.js';
6+
import { FactoryReplyParams, XssSafeFactory } from '../../../common/xss-safe-factory.js';
7+
import { ApiErr } from '../../../common/api/shared/api-error.js';
8+
import { Attachment } from '../../../common/core/attachment.js';
9+
import { BrowserMsg } from '../../../common/browser/browser-msg.js';
10+
import { Catch } from '../../../common/platform/catch.js';
11+
import { GlobalStore, LocalDraft } from '../../../common/platform/store/global-store.js';
12+
import { Injector } from '../../../common/inject.js';
13+
import { PubLookup } from '../../../common/api/pub-lookup.js';
14+
import { Notifications } from '../../../common/notifications.js';
15+
import { PgpArmor } from '../../../common/core/crypto/pgp/pgp-armor.js';
16+
import { Ui } from '../../../common/browser/ui.js';
17+
import { WebmailCommon } from '../../../common/webmail.js';
18+
import { Xss } from '../../../common/platform/xss.js';
19+
import { ClientConfiguration } from '../../../common/client-configuration.js';
2120
// todo: can we somehow define a purely relay class for ContactStore to clearly show that crypto-libraries are not loaded and can't be used?
22-
import { ContactStore } from '../../common/platform/store/contact-store.js';
23-
import { MessageRenderer } from '../../common/message-renderer.js';
24-
import { RelayManager } from '../../common/relay-manager.js';
25-
import { MessageInfo } from '../../common/render-message.js';
21+
import { ContactStore } from '../../../common/platform/store/contact-store.js';
22+
import { MessageRenderer } from '../../../common/message-renderer.js';
23+
import { RelayManager } from '../../../common/relay-manager.js';
24+
import { MessageInfo } from '../../../common/render-message.js';
2625
import { GmailLoaderContext } from './gmail-loader-context.js';
27-
import { JQueryEl } from '../../common/loader-context-interface.js';
28-
import { MessageBody, Mime } from '../../common/core/mime.js';
29-
import { MsgBlock } from '../../common/core/msg-block.js';
30-
import { ReplyOption } from '../../../chrome/elements/compose-modules/compose-reply-btn-popover-module.js';
26+
import { JQueryEl } from '../../../common/loader-context-interface.js';
27+
import { MessageBody, Mime } from '../../../common/core/mime.js';
28+
import { MsgBlock } from '../../../common/core/msg-block.js';
29+
import { ReplyOption } from '../../../../chrome/elements/compose-modules/compose-reply-btn-popover-module.js';
30+
import { WebmailElementReplacer, IntervalFunction } from '../generic/webmail-element-replacer.js';
3131

32-
export class GmailElementReplacer implements WebmailElementReplacer {
32+
export class GmailElementReplacer extends WebmailElementReplacer {
3333
private debug = false;
3434

3535
private recipientHasPgpCache: Dict<boolean> = {};
@@ -74,6 +74,7 @@ export class GmailElementReplacer implements WebmailElementReplacer {
7474
private readonly notifications: Notifications,
7575
private readonly relayManager: RelayManager
7676
) {
77+
super();
7778
this.webmailCommon = new WebmailCommon(acctEmail, injector);
7879
this.pubLookup = new PubLookup(clientConfiguration);
7980
}

extension/js/content_scripts/webmail/gmail-loader-context.ts renamed to extension/js/content_scripts/webmail/gmail/gmail-loader-context.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
'use strict';
44

5-
import { Attachment } from '../../common/core/attachment.js';
6-
import { JQueryEl, LoaderContextInterface } from '../../common/loader-context-interface.js';
7-
import { XssSafeFactory } from '../../common/xss-safe-factory.js';
5+
import { Attachment } from '../../../common/core/attachment.js';
6+
import { JQueryEl, LoaderContextInterface } from '../../../common/loader-context-interface.js';
7+
import { XssSafeFactory } from '../../../common/xss-safe-factory.js';
88

99
export class GmailLoaderContext implements LoaderContextInterface {
1010
public constructor(

0 commit comments

Comments
 (0)