Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
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 actionsMenuBtn = (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 @@ -149,13 +149,21 @@ 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('reply', '#r');
this.addMenuButton('forward', '#r3');
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 = () => {
Expand Down Expand Up @@ -291,13 +299,15 @@ 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
button.on(
'click',
Ui.event.handle((el, ev: JQuery.Event) => this.actionActivateSecureReplyHandler(el, ev))
);
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))
);
}
};

private replaceConvoBtns = (force = false) => {
Expand Down Expand Up @@ -371,7 +381,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;
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 @@ -486,22 +486,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