From 5b116358872fcc2501df4902eade992b4a3a96b2 Mon Sep 17 00:00:00 2001 From: martgil Date: Thu, 23 Jan 2025 15:37:48 +0800 Subject: [PATCH 01/16] Add UI change to add "Secure Reply All" --- extension/img/svgs/reply-all-icon.svg | 1 + extension/js/common/xss-safe-factory.ts | 12 +++++++++--- .../webmail/gmail/gmail-element-replacer.ts | 8 ++++---- extension/manifest.json | 1 + 4 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 extension/img/svgs/reply-all-icon.svg diff --git a/extension/img/svgs/reply-all-icon.svg b/extension/img/svgs/reply-all-icon.svg new file mode 100644 index 00000000000..30828ebd030 --- /dev/null +++ b/extension/img/svgs/reply-all-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extension/js/common/xss-safe-factory.ts b/extension/js/common/xss-safe-factory.ts index 26a97152388..471ed1206e5 100644 --- a/extension/js/common/xss-safe-factory.ts +++ b/extension/js/common/xss-safe-factory.ts @@ -269,9 +269,15 @@ export class XssSafeFactory { `; }; - public actionsMenuBtn = (action: 'reply' | 'forward') => { - return `
- secure ${action} + public actionsMenuBtn = (replyOption: ReplyOption) => { + const actionText = replyOption.replace('a_', '').replace('_', ' '); + const action = { + underscore: actionText.replace(' ', '_'), + hyphen: actionText.replace(' ', '-'), + }; + // * The action_${action.underscoreSeparated}_message_button is used as an identifier in GmailElementReplacer.actionActivateSecureReplyHandler() + return `
+ secure ${actionText}
`; }; diff --git a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts index f2775edc7f4..8413bbc41d1 100644 --- a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts +++ b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts @@ -281,9 +281,9 @@ export class GmailElementReplacer extends WebmailElementReplacer { return !!$('iframe.pgp_block').filter(':visible').length; }; - private addMenuButton = (action: 'reply' | 'forward', selector: string) => { + private addMenuButton = (replyOption: ReplyOption, selector: string) => { const gmailActionsMenuContainer = $(this.sel.msgActionsMenu).find(selector); - const button = $(this.factory.actionsMenuBtn(action)).insertAfter(gmailActionsMenuContainer); // xss-safe-factory + const button = $(this.factory.actionsMenuBtn(replyOption)).insertAfter(gmailActionsMenuContainer); // xss-safe-factory button.on( 'click', Ui.event.handle((el, ev: JQuery.Event) => this.actionActivateSecureReplyHandler(el, ev)) @@ -292,8 +292,8 @@ export class GmailElementReplacer extends WebmailElementReplacer { private replaceActionsMenu = () => { if ($('.action_menu_message_button').length <= 0) { - this.addMenuButton('reply', '#r'); - this.addMenuButton('forward', '#r3'); + this.addMenuButton('a_reply', '#r'); + this.addMenuButton('a_forward', '#r3'); } }; diff --git a/extension/manifest.json b/extension/manifest.json index 8c57be8e8c8..176223d2b23 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -78,6 +78,7 @@ "resources": [ "/css/webmail.css", "/img/svgs/reply-icon.svg", + "/img/svgs/reply-all-icon.svg", "/img/svgs/forward-icon.svg", "/img/svgs/spinner-white-small.svg", "/img/svgs/spinner-green-small.svg", From 2a457f7a95eea3a207e68c20b0c6d750f5d0b5ab Mon Sep 17 00:00:00 2001 From: martgil Date: Thu, 23 Jan 2025 16:40:17 +0800 Subject: [PATCH 02/16] add comment + ready for incoming merge conflict --- .../webmail/gmail/gmail-element-replacer.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts index 8413bbc41d1..bd8cb7d3667 100644 --- a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts +++ b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts @@ -293,6 +293,8 @@ export class GmailElementReplacer extends WebmailElementReplacer { private replaceActionsMenu = () => { if ($('.action_menu_message_button').length <= 0) { this.addMenuButton('a_reply', '#r'); + // * when adding the reply_all button, check if #r2, the placeholder for reply_all, is visible + // * otherwise, it's not wise adding reply_all for single recipient emails this.addMenuButton('a_forward', '#r3'); } }; @@ -368,7 +370,14 @@ export class GmailElementReplacer extends WebmailElementReplacer { private actionActivateSecureReplyHandler = async (btn: HTMLElement, event: JQuery.Event) => { event.stopImmediatePropagation(); const secureReplyInvokedFromMenu = btn.className.includes('action_menu_message_button'); - const replyOption: ReplyOption = btn.className.includes('reply') ? 'a_reply' : 'a_forward'; + let replyOption: ReplyOption; + if (btn.className.includes('reply-all')) { + replyOption = 'a_reply_all'; + } else if (btn.className.includes('forward')) { + replyOption = 'a_forward'; + } else { + replyOption = 'a_reply'; + } if ($('#switch_to_encrypted_reply').length) { $('#switch_to_encrypted_reply').trigger('click'); return; From 72803ff42cf39530dd188bc53f7dbdf22dc0188f Mon Sep 17 00:00:00 2001 From: martgil Date: Fri, 24 Jan 2025 09:34:18 +0800 Subject: [PATCH 03/16] add checking before adding the button to avoid unncessary button --- .../webmail/gmail/gmail-element-replacer.ts | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts index 16cd8845ac2..48abe4c3eb1 100644 --- a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts +++ b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts @@ -151,8 +151,9 @@ export class GmailElementReplacer extends WebmailElementReplacer { $(document).on('click', 'div.aHU.hx', event => { const $actionsBtn = $(event.currentTarget).find(this.sel.msgActionsBtn); if ($actionsBtn.length && !$('.action_menu_message_button').length) { - this.addMenuButton('reply', '#r'); - this.addMenuButton('forward', '#r3'); + this.addMenuButton('a_reply', '#r'); + this.addMenuButton('a_reply_all', '#r2'); + this.addMenuButton('a_forward', '#r3'); } }); }; @@ -292,19 +293,12 @@ export class GmailElementReplacer extends WebmailElementReplacer { private addMenuButton = (replyOption: ReplyOption, selector: string) => { const gmailActionsMenuContainer = $(this.sel.msgActionsMenu).find(selector); - const button = $(this.factory.actionsMenuBtn(replyOption)).insertAfter(gmailActionsMenuContainer); // xss-safe-factory - button.on( - 'click', - Ui.event.handle((el, ev: JQuery.Event) => this.actionActivateSecureReplyHandler(el, ev)) - ); - }; - - private replaceActionsMenu = () => { - if ($('.action_menu_message_button').length <= 0) { - this.addMenuButton('a_reply', '#r'); - // * when adding the reply_all button, check if #r2, the placeholder for reply_all, is visible - // * otherwise, it's not wise adding reply_all for single recipient emails - this.addMenuButton('a_forward', '#r3'); + if ($(selector).css('display') === 'block') { + const button = $(this.factory.actionsMenuBtn(replyOption)).insertAfter(gmailActionsMenuContainer); // xss-safe-factory + button.on( + 'click', + Ui.event.handle((el, ev: JQuery.Event) => this.actionActivateSecureReplyHandler(el, ev)) + ); } }; From eca63a753d00aad0a2469695cb7e56b54408ee06 Mon Sep 17 00:00:00 2001 From: martgil Date: Fri, 24 Jan 2025 10:45:17 +0800 Subject: [PATCH 04/16] update gmail test --- test/source/tests/gmail.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/test/source/tests/gmail.ts b/test/source/tests/gmail.ts index b360f2d0df8..fe76f69adcb 100644 --- a/test/source/tests/gmail.ts +++ b/test/source/tests/gmail.ts @@ -463,22 +463,21 @@ export const defineGmailTests = (testVariant: TestVariant, testWithBrowser: Test await BrowserRecipe.setUpCommonAcct(t, browser, 'ci.tests.gmail'); const gmailPage = await openGmailPage(t, browser); await gotoGmailPage(gmailPage, '/FMfcgzGtwgfMhWTlgRwwKWzRhqNZzwXz'); // go to encrypted convo - await Util.sleep(5); const actionsMenuSelector = '.J-J5-Ji.aap'; await gmailPage.waitAndClick(actionsMenuSelector); - await Util.sleep(3); expect(await gmailPage.isElementPresent('@action-reply-message-button')); + expect(await gmailPage.isElementPresent('@action-reply-all-message-button')); + expect(await gmailPage.isElementPresent('@action-forward-message-button')); await gmailPage.waitAndClick('@action-reply-message-button'); const replyBox = await gmailPage.getFrame(['/chrome/elements/compose.htm'], { sleep: 5 }); - await Util.sleep(3); await replyBox.waitForContent('@input-body', ''); await gmailPage.waitAndClick(actionsMenuSelector); - await Util.sleep(3); - expect(await gmailPage.isElementPresent('@action-forward-message-button')); - await gmailPage.waitAndClick('@action-forward-message-button'); + await gmailPage.waitAndClick('@action-reply-all-message-button'); const replyBox2 = await gmailPage.getFrame(['/chrome/elements/compose.htm'], { sleep: 5 }); - await Util.sleep(3); - await replyBox2.waitForContent('@input-body', '---------- Forwarded message ---------'); + await replyBox2.waitForContent('@input-body', ''); + await gmailPage.waitAndClick('@action-forward-message-button'); + const replyBox3 = await gmailPage.getFrame(['/chrome/elements/compose.htm'], { sleep: 5 }); + await replyBox3.waitForContent('@input-body', '---------- Forwarded message ---------'); }) ); From 5c0118640544ffa33dfd5eb863f8b21c9a1ca6d4 Mon Sep 17 00:00:00 2001 From: martgil Date: Mon, 27 Jan 2025 13:27:12 +0800 Subject: [PATCH 05/16] wip: safe attempt to fix issue --- .../generic/webmail-element-replacer.ts | 1 - .../webmail/gmail/gmail-element-replacer.ts | 24 ++++++++++--------- .../webmail/gmail/gmail-webmail-startup.ts | 1 - 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/extension/js/content_scripts/webmail/generic/webmail-element-replacer.ts b/extension/js/content_scripts/webmail/generic/webmail-element-replacer.ts index 044e101f120..db51b273ac9 100644 --- a/extension/js/content_scripts/webmail/generic/webmail-element-replacer.ts +++ b/extension/js/content_scripts/webmail/generic/webmail-element-replacer.ts @@ -14,7 +14,6 @@ export abstract class WebmailElementReplacer { public abstract reinsertReplyBox: (replyMsgId: string) => void; public abstract scrollToReplyBox: (replyMsgId: string) => void; public abstract scrollToCursorInReplyBox: (replyMsgId: string, cursorOffsetTop: number) => void; - public abstract addSecureActionsToMessageMenu: () => void; public runIntervalFunctionsPeriodically = () => { const intervalFunctions = this.getIntervalFunctions(); diff --git a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts index 48abe4c3eb1..9ff0086ae10 100644 --- a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts +++ b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts @@ -147,17 +147,6 @@ export class GmailElementReplacer extends WebmailElementReplacer { } }; - public addSecureActionsToMessageMenu = () => { - $(document).on('click', 'div.aHU.hx', event => { - const $actionsBtn = $(event.currentTarget).find(this.sel.msgActionsBtn); - if ($actionsBtn.length && !$('.action_menu_message_button').length) { - this.addMenuButton('a_reply', '#r'); - this.addMenuButton('a_reply_all', '#r2'); - this.addMenuButton('a_forward', '#r3'); - } - }); - }; - private everything = () => { this.replaceArmoredBlocks().catch(Catch.reportErr); this.replaceAttachments().catch(Catch.reportErr); @@ -166,6 +155,7 @@ export class GmailElementReplacer extends WebmailElementReplacer { this.replaceStandardReplyBox().catch(Catch.reportErr); this.evaluateStandardComposeRecipients().catch(Catch.reportErr); this.addSettingsBtn(); + this.addSecureActionsToMessageMenu(); this.renderLocalDrafts().catch(Catch.reportErr); }; @@ -879,6 +869,18 @@ export class GmailElementReplacer extends WebmailElementReplacer { } }; + private addSecureActionsToMessageMenu = () => { + $(this.sel.msgActionsBtn) + .off('click') + .on('click', () => { + if (!$('.action_menu_message_button').length) { + this.addMenuButton('a_reply', '#r'); + this.addMenuButton('a_reply_all', '#r2'); + this.addMenuButton('a_forward', '#r3'); + } + }); + }; + private renderLocalDrafts = async () => { if (window.location.hash === '#drafts') { const storage = await GlobalStore.get(['local_drafts']); diff --git a/extension/js/content_scripts/webmail/gmail/gmail-webmail-startup.ts b/extension/js/content_scripts/webmail/gmail/gmail-webmail-startup.ts index a6992cba791..583af380062 100644 --- a/extension/js/content_scripts/webmail/gmail/gmail-webmail-startup.ts +++ b/extension/js/content_scripts/webmail/gmail/gmail-webmail-startup.ts @@ -45,7 +45,6 @@ export class GmailWebmailStartup { const messageRenderer = await MessageRenderer.newInstance(acctEmail, new Gmail(acctEmail), relayManager, factory); this.replacer = new GmailElementReplacer(factory, clientConfiguration, acctEmail, messageRenderer, injector, notifications, relayManager); await notifications.showInitial(acctEmail); - this.replacer.addSecureActionsToMessageMenu(); this.replacer.runIntervalFunctionsPeriodically(); }; From 6feb36e2df3a328e358853b022a9a3e9ed546fad Mon Sep 17 00:00:00 2001 From: martgil Date: Wed, 29 Jan 2025 12:09:32 +0800 Subject: [PATCH 06/16] wip: add retryInterval whenever element is not yet rendered --- .../generic/webmail-element-replacer.ts | 1 + .../webmail/gmail/gmail-element-replacer.ts | 31 +++++++++++-------- .../webmail/gmail/gmail-webmail-startup.ts | 1 + 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/extension/js/content_scripts/webmail/generic/webmail-element-replacer.ts b/extension/js/content_scripts/webmail/generic/webmail-element-replacer.ts index db51b273ac9..044e101f120 100644 --- a/extension/js/content_scripts/webmail/generic/webmail-element-replacer.ts +++ b/extension/js/content_scripts/webmail/generic/webmail-element-replacer.ts @@ -14,6 +14,7 @@ export abstract class WebmailElementReplacer { public abstract reinsertReplyBox: (replyMsgId: string) => void; public abstract scrollToReplyBox: (replyMsgId: string) => void; public abstract scrollToCursorInReplyBox: (replyMsgId: string, cursorOffsetTop: number) => void; + public abstract addSecureActionsToMessageMenu: () => void; public runIntervalFunctionsPeriodically = () => { const intervalFunctions = this.getIntervalFunctions(); diff --git a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts index 9b113ac65f9..417ce60f0a8 100644 --- a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts +++ b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts @@ -148,6 +148,24 @@ export class GmailElementReplacer extends WebmailElementReplacer { } }; + public addSecureActionsToMessageMenu = () => { + const retryInterval = 100; + const addButtons = () => { + if (!$(this.sel.msgActionsBtn).length) { + Catch.setHandledTimeout(addButtons, retryInterval); + } else { + $(document).on('click', 'div.aHU.hx', () => { + if (!$('.action_menu_message_button').length) { + this.addMenuButton('a_reply', '#r'); + this.addMenuButton('a_reply_all', '#r2'); + this.addMenuButton('a_forward', '#r3'); + } + }); + } + }; + addButtons(); + }; + private everything = () => { this.replaceArmoredBlocks().catch(Catch.reportErr); this.replaceAttachments().catch(Catch.reportErr); @@ -156,7 +174,6 @@ export class GmailElementReplacer extends WebmailElementReplacer { this.replaceStandardReplyBox().catch(Catch.reportErr); this.evaluateStandardComposeRecipients().catch(Catch.reportErr); this.addSettingsBtn(); - this.addSecureActionsToMessageMenu(); this.renderLocalDrafts().catch(Catch.reportErr); }; @@ -878,18 +895,6 @@ export class GmailElementReplacer extends WebmailElementReplacer { } }; - private addSecureActionsToMessageMenu = () => { - $(this.sel.msgActionsBtn) - .off('click') - .on('click', () => { - if (!$('.action_menu_message_button').length) { - this.addMenuButton('a_reply', '#r'); - this.addMenuButton('a_reply_all', '#r2'); - this.addMenuButton('a_forward', '#r3'); - } - }); - }; - private renderLocalDrafts = async () => { if (window.location.hash === '#drafts') { const storage = await GlobalStore.get(['local_drafts']); diff --git a/extension/js/content_scripts/webmail/gmail/gmail-webmail-startup.ts b/extension/js/content_scripts/webmail/gmail/gmail-webmail-startup.ts index 583af380062..a6992cba791 100644 --- a/extension/js/content_scripts/webmail/gmail/gmail-webmail-startup.ts +++ b/extension/js/content_scripts/webmail/gmail/gmail-webmail-startup.ts @@ -45,6 +45,7 @@ export class GmailWebmailStartup { const messageRenderer = await MessageRenderer.newInstance(acctEmail, new Gmail(acctEmail), relayManager, factory); this.replacer = new GmailElementReplacer(factory, clientConfiguration, acctEmail, messageRenderer, injector, notifications, relayManager); await notifications.showInitial(acctEmail); + this.replacer.addSecureActionsToMessageMenu(); this.replacer.runIntervalFunctionsPeriodically(); }; From 782971f917603e52515111e01fd0ba4024ed3f37 Mon Sep 17 00:00:00 2001 From: martgil Date: Wed, 29 Jan 2025 17:18:59 +0800 Subject: [PATCH 07/16] wip: fix failing test --- test/source/tests/gmail.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/test/source/tests/gmail.ts b/test/source/tests/gmail.ts index 8566d3056b7..e389837f839 100644 --- a/test/source/tests/gmail.ts +++ b/test/source/tests/gmail.ts @@ -486,18 +486,22 @@ export const defineGmailTests = (testVariant: TestVariant, testWithBrowser: Test await BrowserRecipe.setUpCommonAcct(t, browser, 'ci.tests.gmail'); const gmailPage = await openGmailPage(t, browser); await gotoGmailPage(gmailPage, '/FMfcgzGtwgfMhWTlgRwwKWzRhqNZzwXz'); // go to encrypted convo - const actionsMenuSelector = '.J-J5-Ji.aap'; - await gmailPage.waitAndClick(actionsMenuSelector); + const gmailContextMenu = '.J-J5-Ji.aap'; + await gmailPage.waitAndClick(gmailContextMenu); + await Util.sleep(1); expect(await gmailPage.isElementPresent('@action-reply-message-button')); - expect(await gmailPage.isElementPresent('@action-reply-all-message-button')); - expect(await gmailPage.isElementPresent('@action-forward-message-button')); await gmailPage.waitAndClick('@action-reply-message-button'); const replyBox = await gmailPage.getFrame(['/chrome/elements/compose.htm'], { sleep: 5 }); await replyBox.waitForContent('@input-body', ''); - await gmailPage.waitAndClick(actionsMenuSelector); + await gmailPage.waitAndClick(gmailContextMenu); + await Util.sleep(1); + expect(await gmailPage.isElementPresent('@action-reply-all-message-button')); await gmailPage.waitAndClick('@action-reply-all-message-button'); const replyBox2 = await gmailPage.getFrame(['/chrome/elements/compose.htm'], { sleep: 5 }); await replyBox2.waitForContent('@input-body', ''); + await gmailPage.waitAndClick(gmailContextMenu); + await Util.sleep(1); + expect(await gmailPage.isElementPresent('@action-forward-message-button')); await gmailPage.waitAndClick('@action-forward-message-button'); const replyBox3 = await gmailPage.getFrame(['/chrome/elements/compose.htm'], { sleep: 5 }); await replyBox3.waitForContent('@input-body', '---------- Forwarded message ---------'); From 1a8daf623c46a8c5acb3790c596d6a9f79d2f33e Mon Sep 17 00:00:00 2001 From: martgil Date: Wed, 29 Jan 2025 17:23:18 +0800 Subject: [PATCH 08/16] wip: update comment --- extension/js/common/xss-safe-factory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/js/common/xss-safe-factory.ts b/extension/js/common/xss-safe-factory.ts index 471ed1206e5..d02bf63a8b7 100644 --- a/extension/js/common/xss-safe-factory.ts +++ b/extension/js/common/xss-safe-factory.ts @@ -275,7 +275,7 @@ export class XssSafeFactory { underscore: actionText.replace(' ', '_'), hyphen: actionText.replace(' ', '-'), }; - // * The action_${action.underscoreSeparated}_message_button is used as an identifier in GmailElementReplacer.actionActivateSecureReplyHandler() + // * The action_${action.underscore}_message_button is used as an identifier in GmailElementReplacer.actionActivateSecureReplyHandler() return `
secure ${actionText}
`; From 89a7bd914184dd5b8592dd19a48e1fe93459a7eb Mon Sep 17 00:00:00 2001 From: martgil Date: Thu, 30 Jan 2025 18:04:29 +0800 Subject: [PATCH 09/16] wip:fix secure btn being injected for non-showing regular gmail button --- extension/js/common/xss-safe-factory.ts | 2 +- .../webmail/gmail/gmail-element-replacer.ts | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/extension/js/common/xss-safe-factory.ts b/extension/js/common/xss-safe-factory.ts index d02bf63a8b7..c5da95a3685 100644 --- a/extension/js/common/xss-safe-factory.ts +++ b/extension/js/common/xss-safe-factory.ts @@ -269,7 +269,7 @@ export class XssSafeFactory {
`; }; - public actionsMenuBtn = (replyOption: ReplyOption) => { + public btnSecureMenuBtn = (replyOption: ReplyOption) => { const actionText = replyOption.replace('a_', '').replace('_', ' '); const action = { underscore: actionText.replace(' ', '_'), diff --git a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts index 417ce60f0a8..0387e466b27 100644 --- a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts +++ b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts @@ -299,10 +299,9 @@ export class GmailElementReplacer extends WebmailElementReplacer { return !!$('iframe.pgp_block').filter(':visible').length; }; - private addMenuButton = (replyOption: ReplyOption, selector: string) => { - const gmailActionsMenuContainer = $(this.sel.msgActionsMenu).find(selector); - if ($(selector).css('display') === 'block') { - const button = $(this.factory.actionsMenuBtn(replyOption)).insertAfter(gmailActionsMenuContainer); // xss-safe-factory + private addMenuButton = (replyOption: ReplyOption, gmailContextMenuBtn: string) => { + if ($(gmailContextMenuBtn).css('display') === 'block') { + const button = $(this.factory.btnSecureMenuBtn(replyOption)).insertAfter(gmailContextMenuBtn); // xss-safe-factory button.on( 'click', Ui.event.handle((el, ev: JQuery.Event) => this.actionActivateSecureReplyHandler(el, ev)) From 5fdca479aa9abad910da4c26d1fb290b2499189c Mon Sep 17 00:00:00 2001 From: martgil Date: Mon, 3 Feb 2025 16:55:48 +0800 Subject: [PATCH 10/16] fix delay in showing secure actions menu buttons --- .../generic/webmail-element-replacer.ts | 1 - .../webmail/gmail/gmail-element-replacer.ts | 30 ++++++++----------- .../webmail/gmail/gmail-webmail-startup.ts | 1 - 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/extension/js/content_scripts/webmail/generic/webmail-element-replacer.ts b/extension/js/content_scripts/webmail/generic/webmail-element-replacer.ts index 044e101f120..db51b273ac9 100644 --- a/extension/js/content_scripts/webmail/generic/webmail-element-replacer.ts +++ b/extension/js/content_scripts/webmail/generic/webmail-element-replacer.ts @@ -14,7 +14,6 @@ export abstract class WebmailElementReplacer { public abstract reinsertReplyBox: (replyMsgId: string) => void; public abstract scrollToReplyBox: (replyMsgId: string) => void; public abstract scrollToCursorInReplyBox: (replyMsgId: string, cursorOffsetTop: number) => void; - public abstract addSecureActionsToMessageMenu: () => void; public runIntervalFunctionsPeriodically = () => { const intervalFunctions = this.getIntervalFunctions(); diff --git a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts index 0387e466b27..999eb2b55c7 100644 --- a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts +++ b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts @@ -148,24 +148,6 @@ export class GmailElementReplacer extends WebmailElementReplacer { } }; - public addSecureActionsToMessageMenu = () => { - const retryInterval = 100; - const addButtons = () => { - if (!$(this.sel.msgActionsBtn).length) { - Catch.setHandledTimeout(addButtons, retryInterval); - } else { - $(document).on('click', 'div.aHU.hx', () => { - if (!$('.action_menu_message_button').length) { - this.addMenuButton('a_reply', '#r'); - this.addMenuButton('a_reply_all', '#r2'); - this.addMenuButton('a_forward', '#r3'); - } - }); - } - }; - addButtons(); - }; - private everything = () => { this.replaceArmoredBlocks().catch(Catch.reportErr); this.replaceAttachments().catch(Catch.reportErr); @@ -174,6 +156,7 @@ export class GmailElementReplacer extends WebmailElementReplacer { this.replaceStandardReplyBox().catch(Catch.reportErr); this.evaluateStandardComposeRecipients().catch(Catch.reportErr); this.addSettingsBtn(); + this.addSecureActionsToMessageMenu(); this.renderLocalDrafts().catch(Catch.reportErr); }; @@ -934,4 +917,15 @@ export class GmailElementReplacer extends WebmailElementReplacer { } } }; + + private addSecureActionsToMessageMenu = () => { + const addSecureOptionsToGmail = () => { + if ($(this.sel.msgActionsMenu).is(':visible') && !$('.action_menu_message_button').length) { + this.addMenuButton('a_reply', '#r'); + this.addMenuButton('a_reply_all', '#r2'); + this.addMenuButton('a_forward', '#r3'); + } + }; + Catch.setHandledTimeout(addSecureOptionsToGmail, 100); + }; } diff --git a/extension/js/content_scripts/webmail/gmail/gmail-webmail-startup.ts b/extension/js/content_scripts/webmail/gmail/gmail-webmail-startup.ts index a6992cba791..583af380062 100644 --- a/extension/js/content_scripts/webmail/gmail/gmail-webmail-startup.ts +++ b/extension/js/content_scripts/webmail/gmail/gmail-webmail-startup.ts @@ -45,7 +45,6 @@ export class GmailWebmailStartup { const messageRenderer = await MessageRenderer.newInstance(acctEmail, new Gmail(acctEmail), relayManager, factory); this.replacer = new GmailElementReplacer(factory, clientConfiguration, acctEmail, messageRenderer, injector, notifications, relayManager); await notifications.showInitial(acctEmail); - this.replacer.addSecureActionsToMessageMenu(); this.replacer.runIntervalFunctionsPeriodically(); }; From 8e2e25eb88258d6d3bd797f6a57a7cb12cd044e6 Mon Sep 17 00:00:00 2001 From: martgil Date: Mon, 3 Feb 2025 19:03:19 +0800 Subject: [PATCH 11/16] pr reviews: use .is(':visible') --- .../content_scripts/webmail/gmail/gmail-element-replacer.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts index 999eb2b55c7..3aa80eb3597 100644 --- a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts +++ b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts @@ -283,7 +283,7 @@ export class GmailElementReplacer extends WebmailElementReplacer { }; private addMenuButton = (replyOption: ReplyOption, gmailContextMenuBtn: string) => { - if ($(gmailContextMenuBtn).css('display') === 'block') { + if ($(gmailContextMenuBtn).is(':visible')) { const button = $(this.factory.btnSecureMenuBtn(replyOption)).insertAfter(gmailContextMenuBtn); // xss-safe-factory button.on( 'click', @@ -924,8 +924,10 @@ export class GmailElementReplacer extends WebmailElementReplacer { this.addMenuButton('a_reply', '#r'); this.addMenuButton('a_reply_all', '#r2'); this.addMenuButton('a_forward', '#r3'); + } else { + Catch.setHandledTimeout(addSecureOptionsToGmail, 100); } }; - Catch.setHandledTimeout(addSecureOptionsToGmail, 100); + addSecureOptionsToGmail(); }; } From cd59a745a50f48c98cf6753416d7d86f0865c9b3 Mon Sep 17 00:00:00 2001 From: martgil Date: Tue, 4 Feb 2025 10:21:28 +0800 Subject: [PATCH 12/16] wip: use element observer --- .../webmail/gmail/gmail-element-replacer.ts | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts index 5acae45533f..cba5805f74d 100644 --- a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts +++ b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts @@ -79,6 +79,7 @@ export class GmailElementReplacer extends WebmailElementReplacer { super(); this.webmailCommon = new WebmailCommon(acctEmail, injector); this.pubLookup = new PubLookup(clientConfiguration); + this.setupSecureActionsOnGmailMenu(); } public getIntervalFunctions = (): IntervalFunction[] => { @@ -148,6 +149,19 @@ export class GmailElementReplacer extends WebmailElementReplacer { } }; + public setupSecureActionsOnGmailMenu = () => { + $(document).ready(() => { + const targetSelector = '.b7.J-M'; + const observer = new MutationObserver(() => { + const $element = $(targetSelector); + if ($element.length && $element.is(':visible')) { + this.addSecureActionsToMessageMenu(); + } + }); + observer.observe(document.body, { childList: true, subtree: true }); + }); + }; + private everything = () => { this.replaceArmoredBlocks().catch(Catch.reportErr); this.replaceAttachments().catch(Catch.reportErr); @@ -156,7 +170,6 @@ export class GmailElementReplacer extends WebmailElementReplacer { this.replaceStandardReplyBox().catch(Catch.reportErr); this.evaluateStandardComposeRecipients().catch(Catch.reportErr); this.addSettingsBtn(); - this.addSecureActionsToMessageMenu(); this.renderLocalDrafts().catch(Catch.reportErr); }; @@ -923,15 +936,11 @@ export class GmailElementReplacer extends WebmailElementReplacer { }; private addSecureActionsToMessageMenu = () => { - const addSecureOptionsToGmail = () => { - if ($(this.sel.msgActionsMenu).is(':visible') && !$('.action_menu_message_button').length) { - this.addMenuButton('a_reply', '#r'); - this.addMenuButton('a_reply_all', '#r2'); - this.addMenuButton('a_forward', '#r3'); - } else { - Catch.setHandledTimeout(addSecureOptionsToGmail, 100); - } - }; - addSecureOptionsToGmail(); + if ($('.action_menu_message_button').length) { + return; + } + this.addMenuButton('a_reply', '#r'); + this.addMenuButton('a_reply_all', '#r2'); + this.addMenuButton('a_forward', '#r3'); }; } From eb994d0aba378792e08962b03665dda54bcbe0d8 Mon Sep 17 00:00:00 2001 From: martgil Date: Tue, 4 Feb 2025 10:21:58 +0800 Subject: [PATCH 13/16] wip: update Gmail test duration to 45min --- .semaphore/semaphore.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 1fe5d5102e5..43714b8d68c 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -61,7 +61,7 @@ blocks: run: when: branch = 'master' OR branch =~ 'live-test' OR branch =~ 'gmail-test' execution_time_limit: - minutes: 45 + minutes: 60 task: secrets: - name: flowcrypt-browser-ci-secrets From ec2bd1531055570b50f80bfae301481910a9fc30 Mon Sep 17 00:00:00 2001 From: martgil Date: Tue, 4 Feb 2025 10:45:18 +0800 Subject: [PATCH 14/16] wip: replace .ready() with shorthand jQuery(function() { }) --- .../js/content_scripts/webmail/gmail/gmail-element-replacer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts index cba5805f74d..fe91778814e 100644 --- a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts +++ b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts @@ -150,7 +150,7 @@ export class GmailElementReplacer extends WebmailElementReplacer { }; public setupSecureActionsOnGmailMenu = () => { - $(document).ready(() => { + $(() => { const targetSelector = '.b7.J-M'; const observer = new MutationObserver(() => { const $element = $(targetSelector); From 1e041ddf348d6a52d7fa139c7dae6a43cdfeaa95 Mon Sep 17 00:00:00 2001 From: martgil Date: Tue, 4 Feb 2025 10:57:25 +0800 Subject: [PATCH 15/16] wip: cleanup --- .../content_scripts/webmail/gmail/gmail-element-replacer.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts index fe91778814e..6bf487fb1ab 100644 --- a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts +++ b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts @@ -151,10 +151,9 @@ export class GmailElementReplacer extends WebmailElementReplacer { public setupSecureActionsOnGmailMenu = () => { $(() => { - const targetSelector = '.b7.J-M'; const observer = new MutationObserver(() => { - const $element = $(targetSelector); - if ($element.length && $element.is(':visible')) { + const gmailActionsMenu = $(this.sel.msgActionsMenu); + if (gmailActionsMenu.is(':visible')) { this.addSecureActionsToMessageMenu(); } }); From 0fc5f50718d53a1f356f2b1395cfaf4637751d82 Mon Sep 17 00:00:00 2001 From: martgil Date: Tue, 4 Feb 2025 19:49:27 +0800 Subject: [PATCH 16/16] pr review: use native js code for faster execution --- .../webmail/gmail/gmail-element-replacer.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts index 6bf487fb1ab..379a17104d2 100644 --- a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts +++ b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts @@ -150,15 +150,13 @@ export class GmailElementReplacer extends WebmailElementReplacer { }; public setupSecureActionsOnGmailMenu = () => { - $(() => { - const observer = new MutationObserver(() => { - const gmailActionsMenu = $(this.sel.msgActionsMenu); - if (gmailActionsMenu.is(':visible')) { - this.addSecureActionsToMessageMenu(); - } - }); - observer.observe(document.body, { childList: true, subtree: true }); + const observer = new MutationObserver(() => { + const gmailActionsMenu = document.querySelector(this.sel.msgActionsMenu); + if (gmailActionsMenu && (gmailActionsMenu as HTMLElement).offsetParent !== undefined) { + this.addSecureActionsToMessageMenu(); + } }); + observer.observe(document.body, { childList: true, subtree: true }); }; private everything = () => {