Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion .semaphore/semaphore.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions extension/img/svgs/reply-all-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 9 additions & 3 deletions extension/js/common/xss-safe-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,15 @@ export class XssSafeFactory {
</div>`;
};

public actionsMenuBtn = (action: 'reply' | 'forward') => {
return `<div class="action_${action}_message_button action_menu_message_button" data-test="action-${action}-message-button">
<img src="${this.srcImg(`svgs/${action}-icon.svg`)}" /><span>secure ${action}</span>
public btnSecureMenuBtn = (replyOption: ReplyOption) => {
const actionText = replyOption.replace('a_', '').replace('_', ' ');
const action = {
underscore: actionText.replace(' ', '_'),
hyphen: actionText.replace(' ', '-'),
};
// * The action_${action.underscore}_message_button is used as an identifier in GmailElementReplacer.actionActivateSecureReplyHandler()
return `<div class="action_${action.underscore}_message_button action_menu_message_button" data-test="action-${action.hyphen}-message-button">
<img src="${this.srcImg(`svgs/${action.hyphen}-icon.svg`)}" /><span>secure ${actionText}</span>
</div>`;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
super();
this.webmailCommon = new WebmailCommon(acctEmail, injector);
this.pubLookup = new PubLookup(clientConfiguration);
this.setupSecureActionsOnGmailMenu();
}

public getIntervalFunctions = (): IntervalFunction[] => {
Expand Down Expand Up @@ -148,13 +149,16 @@
}
};

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('reply', '#r');
this.addMenuButton('forward', '#r3');
}
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 });
});
};

Expand Down Expand Up @@ -291,13 +295,14 @@
return !!$('iframe.pgp_block').filter(':visible').length;
};

private addMenuButton = (action: 'reply' | 'forward', selector: string) => {
const gmailActionsMenuContainer = $(this.sel.msgActionsMenu).find(selector);
const button = $(this.factory.actionsMenuBtn(action)).insertAfter(gmailActionsMenuContainer); // xss-safe-factory
button.on(
'click',
Ui.event.handle((el, ev: JQuery.Event) => this.actionActivateSecureReplyHandler(el, ev))
);
private addMenuButton = (replyOption: ReplyOption, gmailContextMenuBtn: string) => {
if ($(gmailContextMenuBtn).is(':visible')) {
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))
);
}
};

private replaceConvoBtns = (force = false) => {
Expand Down Expand Up @@ -371,7 +376,14 @@
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;
Expand Down Expand Up @@ -922,4 +934,13 @@
}
}
};

private addSecureActionsToMessageMenu = () => {
if ($('.action_menu_message_button').length) {
return;
}
this.addMenuButton('a_reply', '#r');
this.addMenuButton('a_reply_all', '#r2');
this.addMenuButton('a_forward', '#r3');
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
};

Expand Down
1 change: 1 addition & 0 deletions extension/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
23 changes: 13 additions & 10 deletions test/source/tests/gmail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -512,22 +512,25 @@ 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);
const gmailContextMenu = '.J-J5-Ji.aap';
await gmailPage.waitAndClick(gmailContextMenu);
await Util.sleep(1);
expect(await gmailPage.isElementPresent('@action-reply-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);
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 replyBox2 = await gmailPage.getFrame(['/chrome/elements/compose.htm'], { sleep: 5 });
await Util.sleep(3);
await replyBox2.waitForContent('@input-body', '---------- Forwarded message ---------');
const replyBox3 = await gmailPage.getFrame(['/chrome/elements/compose.htm'], { sleep: 5 });
await replyBox3.waitForContent('@input-body', '---------- Forwarded message ---------');
})
);

Expand Down