Skip to content

Commit 878f3f1

Browse files
committed
Merge branch 'master' into dependabot/npm_and_yarn/ava-6.1.1
# Conflicts: # package-lock.json # package.json
2 parents 3cf66a4 + 0e00100 commit 878f3f1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1522
-923
lines changed

.github/dependabot.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ updates:
2828
versions: [ ">=1.0.1" ]
2929
- dependency-name: "openpgp"
3030
update-types: ["version-update:semver-major"]
31-
- dependency-name: "web-ext"
32-
update-types: ["version-update:semver-major"]
3331
- dependency-name: "@openpgp/web-stream-tools"
3432
versions: [">= 0.0.14"]
3533
- dependency-name: "ava"
@@ -39,4 +37,6 @@ updates:
3937
- dependency-name: "chai-as-promised"
4038
update-types: ["version-update:semver-major"]
4139
- dependency-name: "eslint"
42-
update-types: ["version-update:semver-major"]
40+
update-types: ["version-update:semver-major"]
41+
- dependency-name: "filesize"
42+
versions: [ "10.1.3", "10.1.4" ]

extension/chrome/elements/compose-modules/compose-quote-module.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { ViewModule } from '../../../js/common/view-module.js';
1717
import { ComposeView } from '../compose.js';
1818
import { MessageToReplyOrForward } from './compose-types.js';
1919
import { KeyStore } from '../../../js/common/platform/store/key-store.js';
20+
import { Time } from '../../../js/common/browser/time.js';
2021

2122
export class ComposeQuoteModule extends ViewModule<ComposeView> {
2223
public tripleDotSanitizedHtmlContent: { quote: string | undefined; footer: string | undefined } | undefined;
@@ -177,6 +178,9 @@ export class ComposeQuoteModule extends ViewModule<ComposeView> {
177178
if (decryptRes.success) {
178179
return decryptRes.content.toUtfStr();
179180
} else if (decryptRes.error && decryptRes.error.type === DecryptErrTypes.needPassphrase) {
181+
if (Catch.isThunderbirdMail() && this.view.useFullScreenSecureCompose) {
182+
await Time.sleep(2300);
183+
}
180184
BrowserMsg.send.passphraseDialog(this.view.parentTabId, {
181185
type: 'quote',
182186
longids: decryptRes.longids.needPassphrase,
@@ -206,16 +210,17 @@ export class ComposeQuoteModule extends ViewModule<ComposeView> {
206210
return;
207211
}
208212
const text = this.messageToReplyOrForward.text;
209-
const from = this.messageToReplyOrForward.headers.from;
213+
const from = Str.parseEmail(this.messageToReplyOrForward.headers.from || '').email;
210214
const date = new Date(String(this.messageToReplyOrForward.headers.date));
211215
const dateStr = Str.fromDate(date).replace(' ', ' at ');
212-
const rtl = text.match(new RegExp('[' + Str.rtlChars + ']'));
216+
const rtl = new RegExp('[' + Str.rtlChars + ']').exec(text);
213217
const dirAttr = `dir="${rtl ? 'rtl' : 'ltr'}"`;
214218
const escapedText = this.convertLineBreakToBr(Xss.escape(text), method === 'reply');
215219
if (method === 'reply') {
216220
const header = `<div ${dirAttr}>On ${dateStr}, ${from ?? ''} wrote:</div>`;
217221
const sanitizedQuote = Xss.htmlSanitize(header + escapedText);
218-
return `<blockquote ${dirAttr}>${sanitizedQuote}</blockquote>`;
222+
const thunderbirdClass = this.view.useFullScreenSecureCompose ? 'class="height-0"' : ''; // fix long quoted email UI issue happens in fullscreen
223+
return `<blockquote ${thunderbirdClass} ${dirAttr}>${sanitizedQuote}</blockquote>`;
219224
} else {
220225
const header =
221226
`<div ${dirAttr}>` +

extension/chrome/elements/compose-modules/compose-render-module.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ export class ComposeRenderModule extends ViewModule<ComposeView> {
2727
private previousReplyOption: ReplyOption | undefined;
2828

2929
public initComposeBox = async () => {
30-
if (this.view.isReplyBox) {
30+
if (this.view.useFullScreenSecureCompose) {
31+
this.view.S.cached('body').addClass('full_window');
32+
this.view.S.cached('password_or_pubkey').height(1); // fix UI issue in fullscreen
33+
}
34+
if (this.view.replyMsgId) {
3135
this.responseMethod = 'reply';
3236
}
3337
await this.view.replyPopoverModule.render(this.view.isReplyBox);
@@ -41,11 +45,11 @@ export class ComposeRenderModule extends ViewModule<ComposeView> {
4145
this.view.draftModule.startDraftTimer();
4246
this.view.S.cached('triple_dot').remove(); // if it's draft, footer and quote should already be included in the draft
4347
}
44-
if (this.view.isReplyBox) {
48+
if (this.view.replyMsgId) {
4549
await this.view.renderModule.renderReplyMsgComposeTable();
4650
}
4751
} else {
48-
if (this.view.isReplyBox && this.view.replyParams) {
52+
if (this.view.replyMsgId && this.view.replyParams) {
4953
const recipients: Recipients = {
5054
to: this.view.replyParams.to,
5155
cc: this.view.replyParams.cc,
@@ -99,6 +103,9 @@ export class ComposeRenderModule extends ViewModule<ComposeView> {
99103
)?.value;
100104
}
101105
this.view.replyParams.subject = `${this.responseMethod === 'reply' ? 'Re' : 'Fwd'}: ${this.view.replyParams.subject}`;
106+
if (this.view.useFullScreenSecureCompose) {
107+
this.view.S.cached('input_subject').val(this.view.replyParams.subject);
108+
}
102109
}
103110
if (!this.view.draftModule.wasMsgLoadedFromDraft) {
104111
// if there is a draft, don't attempt to pull quoted content. It's assumed to be already present in the draft
@@ -260,6 +267,12 @@ export class ComposeRenderModule extends ViewModule<ComposeView> {
260267
await this.renderReplyMsgComposeTable();
261268
};
262269

270+
public actionCloseHandler = async () => {
271+
if (!this.view.sendBtnModule.isSendMessageInProgres() || (await Ui.modal.confirm(Lang.compose.abortSending))) {
272+
this.view.renderModule.closeMsg();
273+
}
274+
};
275+
263276
private initComposeBoxStyles = () => {
264277
if (this.view.isReplyBox) {
265278
this.view.S.cached('body').addClass('reply_box');
@@ -328,16 +341,12 @@ export class ComposeRenderModule extends ViewModule<ComposeView> {
328341
this.view.S.cached('compose_table').css('display', 'table');
329342
await this.addComposeTableHandlers();
330343
await this.view.senderModule.renderSendFromIfMoreThanOneAlias();
331-
if (this.view.isReplyBox) {
344+
if (this.view.replyMsgId) {
332345
if (this.view.replyParams?.to.length) {
333346
// Firefox will not always respond to initial automatic $input_text.blur(): recipients may be left unrendered, as standard text, with a trailing comma
334347
await this.view.recipientsModule.parseRenderRecipients(this.view.S.cached('input_to')); // this will force firefox to render them on load
335348
}
336349
} else {
337-
$('.close_compose_window').on(
338-
'click',
339-
this.view.setHandler(() => this.actionCloseHandler(), this.view.errModule.handle(`close compose window`))
340-
);
341350
this.view.S.cached('title').on('click', () => {
342351
if (this.view.sizeModule.composeWindowIsMinimized) {
343352
$('.minimize_compose_window').trigger('click');
@@ -400,12 +409,6 @@ export class ComposeRenderModule extends ViewModule<ComposeView> {
400409
}
401410
};
402411

403-
private actionCloseHandler = async () => {
404-
if (!this.view.sendBtnModule.isSendMessageInProgres() || (await Ui.modal.confirm(Lang.compose.abortSending))) {
405-
this.view.renderModule.closeMsg();
406-
}
407-
};
408-
409412
private onRecipientsClickHandler = () => {
410413
if (!this.view.S.cached('input_to').is(':focus')) {
411414
this.view.errModule.debug(
@@ -422,7 +425,7 @@ export class ComposeRenderModule extends ViewModule<ComposeView> {
422425
let normalizedPub: string;
423426
try {
424427
normalizedPub = await keyImportUi.checkPub(textData);
425-
} catch (e) {
428+
} catch {
426429
return; // key is invalid
427430
}
428431
const key = await KeyUtil.parse(normalizedPub);

extension/chrome/elements/compose-modules/compose-reply-btn-popover-module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ export class ComposeReplyBtnPopoverModule extends ViewModule<ComposeView> {
4545
};
4646

4747
public changeOptionImage = (option: ReplyOption) => {
48-
$('.reply-options-icon').attr('src', this.popoverItems[option].iconPath);
48+
if (!this.view.useFullScreenSecureCompose) {
49+
$('.reply-options-icon').attr('src', this.popoverItems[option].iconPath);
50+
}
4951
};
5052

5153
private didOptionClick = async (option: ReplyOption) => {

extension/chrome/elements/compose.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export class ComposeView extends View {
4141
public readonly removeAfterClose: boolean;
4242
public readonly disableDraftSaving: boolean;
4343
public readonly debug: boolean;
44+
public readonly useFullScreenSecureCompose: boolean;
4445
public readonly isReplyBox: boolean;
4546
public readonly replyMsgId: string;
4647
public readonly replyPubkeyMismatch: boolean;
@@ -108,6 +109,7 @@ export class ComposeView extends View {
108109
toggle_send_options: '#toggle_send_options',
109110
toggle_reply_options: '#toggle_reply_options',
110111
icon_pubkey: '.icon.action_include_pubkey',
112+
close_compose_window: '.close_compose_window',
111113
icon_help: '.action_feedback',
112114
icon_popout: '.popout img',
113115
triple_dot: '.action_show_prev_msg',
@@ -148,6 +150,7 @@ export class ComposeView extends View {
148150
'removeAfterClose',
149151
'replyPubkeyMismatch',
150152
'replyOption',
153+
'useFullScreenSecureCompose',
151154
]);
152155
this.acctEmail = Assert.urlParamRequire.string(uncheckedUrlParams, 'acctEmail');
153156
this.parentTabId = Assert.urlParamRequire.string(uncheckedUrlParams, 'parentTabId');
@@ -161,7 +164,8 @@ export class ComposeView extends View {
161164
this.replyPubkeyMismatch = uncheckedUrlParams.replyPubkeyMismatch === true;
162165
this.draftId = Assert.urlParamRequire.optionalString(uncheckedUrlParams, 'draftId') || '';
163166
this.replyMsgId = Assert.urlParamRequire.optionalString(uncheckedUrlParams, 'replyMsgId') || '';
164-
this.isReplyBox = !!this.replyMsgId;
167+
this.useFullScreenSecureCompose = uncheckedUrlParams.useFullScreenSecureCompose === true;
168+
this.isReplyBox = !!this.replyMsgId && !this.useFullScreenSecureCompose;
165169
this.emailProvider = new Gmail(this.acctEmail);
166170
this.acctServer = new AccountServer(this.acctEmail);
167171
}
@@ -201,7 +205,7 @@ export class ComposeView extends View {
201205
}
202206
BrowserMsg.listen(this.tabId);
203207
await this.renderModule.initComposeBox();
204-
if (this.replyOption && this.isReplyBox) {
208+
if (this.replyOption && this.replyMsgId) {
205209
await this.renderModule.activateReplyOption(this.replyOption, true);
206210
}
207211
this.senderModule.checkEmailAliases().catch(Catch.reportErr);
@@ -225,6 +229,10 @@ export class ComposeView extends View {
225229
});
226230
this.S.cached('body').on('focusin', setActiveWindow);
227231
this.S.cached('body').on('click', setActiveWindow);
232+
this.S.cached('close_compose_window').on(
233+
'click',
234+
this.setHandler(async () => await this.renderModule.actionCloseHandler(), this.errModule.handle(`close compose window`))
235+
);
228236
this.S.cached('icon_help').on(
229237
'click',
230238
this.setHandler(async () => await this.renderModule.openSettingsWithDialog('help'), this.errModule.handle(`help dialog`))

extension/chrome/elements/pgp_block.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ export class PgpBlockView extends View {
6767
BrowserMsg.addListener('confirmation_result', CommonHandlers.createAsyncResultHandler());
6868
BrowserMsg.listen(this.getDest());
6969
BrowserMsg.send.pgpBlockReady(this, { frameId: this.frameId, messageSender: this.getDest() });
70+
// Added this listener to handle cases where 'inbox_page/setup-webmail-content-script' is not ready to retrieve 'pgpBlockReady' events.
71+
// This can occur if 'setHandlers' is called before 'Inbox.setHandlers' is fully initialized.
72+
// https://github.com/FlowCrypt/flowcrypt-browser/pull/5783#discussion_r1663636264
73+
BrowserMsg.addListener('set_handler_ready_for_pgp_block', async () => {
74+
BrowserMsg.send.pgpBlockReady(this, { frameId: this.frameId, messageSender: this.getDest() });
75+
});
7076
};
7177

7278
private renderProgress = ({ operationId, text, perc, init }: { operationId: string; text: string; perc?: number; init?: boolean }) => {

extension/chrome/elements/shared/key_errors.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export class KeyErrors {
2424

2525
public handlePrivateKeyError = async (exception: unknown, origPrv: Key, setupOptions?: SetupOptions) => {
2626
if (exception instanceof UserAlert) {
27-
await Ui.modal.warning(exception.message, Ui.testCompatibilityLink);
27+
await Ui.modal.warning(exception.message, Ui.getTestCompatibilityLink(this.acctEmail));
2828
return;
2929
} else if (exception instanceof KeyCanBeFixed) {
3030
await this.renderCompatibilityFixBlockAndFinalizeSetup(origPrv, setupOptions);
@@ -37,7 +37,7 @@ export class KeyErrors {
3737
await Ui.modal.error(
3838
`An error happened when processing the key: ${String(exception)}\n${Lang.general.contactForSupportSentence(this.isCustomerUrlFesUsed())}`,
3939
false,
40-
Ui.testCompatibilityLink
40+
Ui.getTestCompatibilityLink(this.acctEmail)
4141
);
4242
return;
4343
}
@@ -93,7 +93,11 @@ export class KeyErrors {
9393
await this.saveKeyAndContinue(fixedPrv);
9494
} catch (e) {
9595
Catch.reportErr(e);
96-
await Ui.modal.error(`Failed to fix key (${String(e)}). ${Lang.general.writeMeToFixIt(this.isCustomerUrlFesUsed())}`, false, Ui.testCompatibilityLink);
96+
await Ui.modal.error(
97+
`Failed to fix key (${String(e)}). ${Lang.general.writeMeToFixIt(this.isCustomerUrlFesUsed())}`,
98+
false,
99+
Ui.getTestCompatibilityLink(this.acctEmail)
100+
);
97101
if (this.setupView) {
98102
this.setupView.setupRender.displayBlock('step_2b_manual_enter');
99103
} else {

extension/chrome/settings/inbox/inbox.htm

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717

1818
<body id="settings" class="client">
1919
<div class="line header" id="banner">
20-
This page has limited functionality. Using FlowCrypt directly in Gmail is more convenient:
21-
<a href="https://mail.google.com/" class="action_open_webmail">Open Gmail</a>
20+
<div id="container-gmail-banner">
21+
This page has limited functionality. Using FlowCrypt directly in Gmail is more convenient:
22+
<a href="https://mail.google.com/" class="action_open_webmail">Open Gmail</a>
23+
</div>
2224

2325
<img src="/img/svgs/profile-icon.svg" class="action_choose_account action-toggle-accounts-menu profile-img main-profile-img" alt="Profile Image" />
2426
<img src="/img/svgs/settings-icon.svg" class="action_open_settings" alt="Settings" title="Open FlowCrypt Settings" />

extension/chrome/settings/inbox/inbox.ts

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ export class InboxView extends View {
3636
public readonly threadId: string | undefined;
3737
public readonly showOriginal: boolean;
3838
public readonly debug: boolean;
39+
public readonly useFullScreenSecureCompose: boolean;
40+
public readonly thunderbirdMsgId: number | undefined;
41+
public readonly composeMethod: 'reply' | 'forward' | undefined;
3942
public readonly S: SelCache;
4043
public readonly gmail: Gmail;
4144

@@ -49,12 +52,24 @@ export class InboxView extends View {
4952

5053
public constructor() {
5154
super();
52-
const uncheckedUrlParams = Url.parse(['acctEmail', 'labelId', 'threadId', 'showOriginal', 'debug']);
55+
const uncheckedUrlParams = Url.parse([
56+
'acctEmail',
57+
'labelId',
58+
'threadId',
59+
'showOriginal',
60+
'debug',
61+
'useFullScreenSecureCompose',
62+
'composeMethod',
63+
'thunderbirdMsgId',
64+
]);
5365
this.acctEmail = Assert.urlParamRequire.string(uncheckedUrlParams, 'acctEmail');
5466
this.labelId = uncheckedUrlParams.labelId ? String(uncheckedUrlParams.labelId) : 'INBOX';
5567
this.threadId = Assert.urlParamRequire.optionalString(uncheckedUrlParams, 'threadId');
5668
this.showOriginal = uncheckedUrlParams.showOriginal === true;
5769
this.debug = uncheckedUrlParams.debug === true;
70+
this.useFullScreenSecureCompose = uncheckedUrlParams.useFullScreenSecureCompose === true;
71+
this.composeMethod = (Assert.urlParamRequire.optionalString(uncheckedUrlParams, 'composeMethod') as 'reply') || 'forward';
72+
this.thunderbirdMsgId = Number(Assert.urlParamRequire.optionalString(uncheckedUrlParams, 'thunderbirdMsgId'));
5873
this.S = Ui.buildJquerySels({ threads: '.threads', thread: '.thread', body: 'body' });
5974
this.gmail = new Gmail(this.acctEmail);
6075
this.inboxMenuModule = new InboxMenuModule(this);
@@ -72,6 +87,11 @@ export class InboxView extends View {
7287
({ email_provider: emailProvider, picture: this.picture } = await AcctStore.get(this.acctEmail, ['email_provider', 'picture']));
7388
this.messageRenderer = await MessageRenderer.newInstance(this.acctEmail, this.gmail, this.relayManager, this.factory, this.debug);
7489
this.inboxNotificationModule.render();
90+
const parsedThreadId = await this.parseThreadIdFromHeaderMessageId();
91+
this.preRenderSecureComposeInFullScreen(parsedThreadId);
92+
if (Catch.isThunderbirdMail()) {
93+
$('#container-gmail-banner').hide();
94+
}
7595
try {
7696
await Settings.populateAccountsMenu('inbox.htm');
7797
if (emailProvider && emailProvider !== 'gmail') {
@@ -81,7 +101,11 @@ export class InboxView extends View {
81101
if (this.threadId) {
82102
await this.inboxActiveThreadModule.render(this.threadId);
83103
} else {
84-
await this.inboxListThreadsModule.render(this.labelId);
104+
if (parsedThreadId && !this.useFullScreenSecureCompose) {
105+
await this.inboxActiveThreadModule.render(parsedThreadId);
106+
} else {
107+
await this.inboxListThreadsModule.render(this.labelId);
108+
}
85109
}
86110
}
87111
} catch (e) {
@@ -99,6 +123,7 @@ export class InboxView extends View {
99123
public setHandlers = () => {
100124
this.addBrowserMsgListeners();
101125
BrowserMsg.listen(this.tabId);
126+
BrowserMsg.send.setHandlerReadyForPGPBlock('broadcast');
102127
Catch.setHandledInterval(this.webmailCommon.addOrRemoveEndSessionBtnIfNeeded, 30000);
103128
$('.action_open_settings').on(
104129
'click',
@@ -132,6 +157,31 @@ export class InboxView extends View {
132157
Xss.sanitizeRender('h1', title);
133158
};
134159

160+
private preRenderSecureComposeInFullScreen = (replyMsgId?: string) => {
161+
if (this.useFullScreenSecureCompose && this.composeMethod) {
162+
const replyOption = this.composeMethod === 'reply' ? 'a_reply' : 'a_forward';
163+
this.injector.openComposeWin(undefined, true, this.thunderbirdMsgId, replyOption, replyMsgId);
164+
}
165+
};
166+
167+
private parseThreadIdFromHeaderMessageId = async () => {
168+
let threadId;
169+
if (Catch.isThunderbirdMail() && this.thunderbirdMsgId) {
170+
const { headers } = await messenger.messages.getFull(this.thunderbirdMsgId);
171+
if (headers) {
172+
const messageId = headers['message-id'][0];
173+
if (messageId) {
174+
const query = `rfc822msgid:${messageId}`;
175+
const gmailRes = await this.gmail.msgList(query);
176+
if (gmailRes.messages) {
177+
threadId = gmailRes.messages[0].threadId;
178+
}
179+
}
180+
}
181+
}
182+
return threadId;
183+
};
184+
135185
private addBrowserMsgListeners = () => {
136186
BrowserMsg.addListener('add_end_session_btn', () => this.injector.insertEndSessionBtn(this.acctEmail));
137187
BrowserMsg.addListener('set_active_window', async ({ frameId }: Bm.ComposeWindow) => {

extension/chrome/settings/initial.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ View.run(
1111
const browserName = Catch.browser().name === 'chrome' && Number(Catch.browser().v) >= 76 ? 'chrome' : Catch.browser().name;
1212
if (browserName === 'thunderbird') {
1313
$('#img-setup-arrow').hide();
14+
$('#thunderbird-steps').parent().parent().css('text-align', 'center');
1415
}
1516
const stepsEl = document.getElementById(`${browserName}-steps`);
1617
if (stepsEl) {

0 commit comments

Comments
 (0)