Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#5666 Add initial support for Thunderbird (improved setup flow) #5690

Merged
merged 19 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
3abf78e
Better FlowCrypt set up flow for Thunderbird
martgil Apr 27, 2024
3cd22c2
Update app description for Thunderbird
martgil Apr 27, 2024
1d274f4
Fix paths on manifest.json
martgil Apr 27, 2024
0c57369
Merge remote-tracking branch 'origin/master' into issue-5666-5667-add…
martgil May 15, 2024
56f731f
Update manifest.json build for thunderbird
martgil May 15, 2024
96ad572
Merge branch 'master' into issue-5666-5667-add-thunderbird-support
martgil May 16, 2024
d01ea80
Merge branch 'master' into issue-5666-5667-add-thunderbird-support
martgil May 21, 2024
4100d70
Merge branch 'master' into issue-5666-5667-add-thunderbird-support
martgil May 26, 2024
3945c15
Merge branch 'master' into issue-5666-5667-add-thunderbird-support
tomholub May 28, 2024
1f2323c
Merge branch 'master' into issue-5666-5667-add-thunderbird-support
martgil May 29, 2024
c37eda4
Merge branch 'master' into issue-5666-5667-add-thunderbird-support
martgil May 30, 2024
ea52711
Pre-configure setup for content scripts
martgil Jun 3, 2024
652d576
Merge branch 'master' into issue-5666-5667-add-thunderbird-support
martgil Jun 3, 2024
4568417
Deprecated Browser.openExtensionTab and use modern BgUtils.openExtens…
martgil Jun 4, 2024
9c06611
Fix code that results to a failing test
martgil Jun 4, 2024
c2752b9
Adjust manifest.json permissions for XHR requests
martgil Jun 4, 2024
c467870
Pr review: improved way of opening extensions link per browser
martgil Jun 5, 2024
ca6b352
Pr review: enable manifest.json reuse
martgil Jun 5, 2024
fda413e
Pr review: cleanup
martgil Jun 5, 2024
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
6 changes: 5 additions & 1 deletion extension/chrome/settings/initial.htm
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,13 @@
Click the <img src="/img/logo/flowcrypt-logo-64-64.png" style="filter: grayscale(0.75); position: relative; top: 15px" /> button in the
corner<br />
</div>
<div id="thunderbird-steps" style="display: none">
Go to Thunderbird Mail and click the <img src="/img/logo/flowcrypt-logo-64-64.png" style="position: relative; top: 15px" /> button to get
started
</div>
</div>
</td>
<td width="350" style="text-align: center; padding-right: 50px; padding-top: 25px">
<td id="img-setup-arrow" width="350" style="text-align: center; padding-right: 50px; padding-top: 25px">
<img src="/img/fa/fa-location-arrow.png" />
</td>
</tr>
Expand Down
5 changes: 4 additions & 1 deletion extension/chrome/settings/initial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import { View } from '../../js/common/view.js';
View.run(
class InitialView extends View {
public render = async () => {
const browserName = Catch.browser().name === 'chrome' && Number(Catch.browser().v) >= 76 ? 'chrome' : 'firefox';
const browserName = Catch.browser().name === 'chrome' && Number(Catch.browser().v) >= 76 ? 'chrome' : Catch.browser().name;
if (browserName === 'thunderbird') {
$('#img-setup-arrow').hide();
}
const stepsEl = document.getElementById(`${browserName}-steps`);
if (stepsEl) {
stepsEl.style.display = 'block';
Expand Down
19 changes: 12 additions & 7 deletions extension/js/common/browser/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Attachment } from '../core/attachment.js';
import { Catch } from '../platform/catch.js';
import { Dict, Url, UrlParam } from '../core/common.js';
import { GlobalStore } from '../platform/store/global-store.js';
import { BgUtils } from '../../service_worker/bgutils.js';

export class Browser {
public static objUrlCreate = (content: Uint8Array | string) => {
Expand Down Expand Up @@ -56,19 +57,23 @@ export class Browser {
const basePath = chrome.runtime.getURL(`chrome/settings/${path}`);
const pageUrlParams = rawPageUrlParams ? JSON.stringify(rawPageUrlParams) : undefined;
if (acctEmail || path === 'fatal.htm') {
Browser.openExtensionTab(Url.create(basePath, { acctEmail, page, pageUrlParams }));
await Browser.openExtensionTab(Url.create(basePath, { acctEmail, page, pageUrlParams }));
} else if (addNewAcct) {
Browser.openExtensionTab(Url.create(basePath, { addNewAcct }));
await Browser.openExtensionTab(Url.create(basePath, { addNewAcct }));
} else {
const acctEmails = await GlobalStore.acctEmailsGet();
Browser.openExtensionTab(Url.create(basePath, { acctEmail: acctEmails[0], page, pageUrlParams }));
await Browser.openExtensionTab(Url.create(basePath, { acctEmail: acctEmails[0], page, pageUrlParams }));
}
};

public static openExtensionTab = (url: string) => {
const tab = window.open(url, 'flowcrypt');
if (tab) {
tab.focus();
public static openExtensionTab = async (url: string) => {
if (Catch.browser().name === 'thunderbird') {
await BgUtils.openExtensionTab(url);
} else {
const tab = window.open(url, 'flowcrypt');
if (tab) {
tab.focus();
}
}
};
}
2 changes: 1 addition & 1 deletion extension/js/common/browser/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { Url } from '../core/common.js';

export type WebMailName = 'gmail' | 'outlook' | 'settings';
export type WebMailName = 'gmail' | 'thunderbird' | 'outlook' | 'settings';
export type WebMailVersion = 'generic' | 'gmail2020' | 'gmail2022';

export class Env {
Expand Down
3 changes: 3 additions & 0 deletions extension/js/common/inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { PassphraseStore } from './platform/store/passphrase-store.js';

type Host = {
gmail: string;
thunderbird: string;
outlook: string;
settings: string;
};
Expand All @@ -26,11 +27,13 @@ export class Injector {
private container: { [key: string]: Host } = {
composeBtnSel: {
gmail: 'div.aeN, div.aBO', // .aeN for normal look, .aBO for new look https://github.com/FlowCrypt/flowcrypt-browser/issues/4099
thunderbird: '', // todo in another issue
outlook: 'div._fce_b',
settings: '#does_not_have',
},
finishSesionBtnSel: {
gmail: 'body',
thunderbird: '', // todo in another issue
outlook: '#does_not_have',
settings: '#settings > div.header',
},
Expand Down
4 changes: 3 additions & 1 deletion extension/js/common/platform/catch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,14 @@ export class Catch {
}

public static browser(): {
name: 'firefox' | 'ie' | 'chrome' | 'opera' | 'safari' | 'unknown';
name: 'firefox' | 'thunderbird' | 'ie' | 'chrome' | 'opera' | 'safari' | 'unknown';
v: number | undefined;
} {
// http://stackoverflow.com/questions/4825498/how-can-i-find-out-which-browser-a-user-is-using
if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent)) {
return { name: 'firefox', v: Number(RegExp.$1) };
} else if (/Thunderbird[\/\s](\d+\.\d+)/.test(navigator.userAgent)) {
return { name: 'thunderbird', v: Number(RegExp.$1) };
} else if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) {
return { name: 'ie', v: Number(RegExp.$1) };
} else if (/Chrome[\/\s](\d+\.\d+)/.test(navigator.userAgent)) {
Expand Down
2 changes: 1 addition & 1 deletion extension/js/common/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ export class Settings {
public static async loginWithPopupShowModalOnErr(acctEmail: string, then: () => void = () => undefined) {
if (window !== window.top && !chrome.windows) {
// Firefox, chrome.windows isn't available in iframes
Browser.openExtensionTab(Url.create(chrome.runtime.getURL(`chrome/settings/index.htm`), { acctEmail }));
await Browser.openExtensionTab(Url.create(chrome.runtime.getURL(`chrome/settings/index.htm`), { acctEmail }));
await Ui.modal.info(`Reload after logging in.`);
return window.location.reload();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* ©️ 2016 - present FlowCrypt a.s. Limitations apply. Contact [email protected] */

'use strict';

import { IntervalFunction, WebmailElementReplacer } from '../generic/webmail-element-replacer';

export class ThunderbirdElementReplacer extends WebmailElementReplacer {
public getIntervalFunctions: () => IntervalFunction[];
public setReplyBoxEditable: () => Promise<void>;
public reinsertReplyBox: (replyMsgId: string) => void;
public scrollToReplyBox: (replyMsgId: string) => void;
public scrollToCursorInReplyBox: (replyMsgId: string, cursorOffsetTop: number) => void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* ©️ 2016 - present FlowCrypt a.s. Limitations apply. Contact [email protected] */
import { ClientConfiguration } from '../../../common/client-configuration';
import { Injector } from '../../../common/inject';
import { Notifications } from '../../../common/notifications';
import { contentScriptSetupIfVacant } from '../generic/setup-webmail-content-script';
import { GmailElementReplacer } from '../gmail/gmail-element-replacer';
import { ThunderbirdElementReplacer } from './thunderbird-element-replacer';

export class ThunderbirdWebmailStartup {
private replacer: GmailElementReplacer;

public asyncConstructor = async () => {
await contentScriptSetupIfVacant({
name: 'thunderbird',
variant: undefined,
getUserAccountEmail: () => undefined, // todo, but can start with undefined
getUserFullName: () => undefined, // todo, but can start with undefined
getReplacer: () => new ThunderbirdElementReplacer(), // todo - add this class empty, methods do nothing
start: this.start,
});
};

private start = async (
acctEmail: string,
clientConfiguration: ClientConfiguration,
injector: Injector,
notifications: Notifications
// factory: XssSafeFactory, // todo in another issue
// relayManager: RelayManager // todo in another issue
) => {
// injector.btns(); // todo in another issue - add compose button
this.replacer.runIntervalFunctionsPeriodically();
await notifications.showInitial(acctEmail);
notifications.show(
'FlowCrypt Thunderbird support is still in early development, and not expected to function properly yet. Support will be gradually added in upcoming versions.'
);
};
}
8 changes: 7 additions & 1 deletion extension/js/content_scripts/webmail/webmail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import { Catch } from '../../common/platform/catch.js';
import { GmailWebmailStartup } from './gmail/gmail-webmail-startup.js';
import { ThunderbirdWebmailStartup } from './thunderbird/thunderbird-webmail-startup.js';

declare global {
interface Window {
Expand All @@ -16,5 +17,10 @@ declare global {

Catch.try(async () => {
// when we support more webmails, there will be if/else here to figure out which one to run
await new GmailWebmailStartup().asyncConstructor();
const browserName = Catch.browser().name;
if (browserName === 'thunderbird') {
await new ThunderbirdWebmailStartup().asyncConstructor();
} else {
await new GmailWebmailStartup().asyncConstructor();
}
})();
4 changes: 3 additions & 1 deletion scripts/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,13 @@ main() {
synchronize_files "build/chrome-enterprise"
synchronize_files "build/chrome-consumer"
synchronize_files "build/firefox-consumer"
synchronize_files "build/thunderbird-consumer"
}
exit 0
fi

if [[ "$#" == 1 ]] && [[ "$1" == "--incremental" ]]; then
delete_directories $BUILD_DIRECTORY/firefox-consumer $BUILD_DIRECTORY/chrome-consumer $BUILD_DIRECTORY/chrome-consumer-mock $BUILD_DIRECTORY/chrome-enterprise $BUILD_DIRECTORY/chrome-enterprise-mock $BUILD_DIRECTORY/generic-extension-wip/js/content_scripts
delete_directories $BUILD_DIRECTORY/firefox-consumer $BUILD_DIRECTORY/thunderbird-consumer $BUILD_DIRECTORY/chrome-consumer $BUILD_DIRECTORY/chrome-consumer-mock $BUILD_DIRECTORY/chrome-enterprise $BUILD_DIRECTORY/chrome-enterprise-mock $BUILD_DIRECTORY/generic-extension-wip/js/content_scripts
# build concurrently - using standard typescript compiler with --incremental flag
mkdir -p $BUILD_DIRECTORY
build_typescript_project false "./tsconfig.json" true "$BUILD_DIRECTORY/tsconfig.tsbuildinfo"
Expand Down Expand Up @@ -150,6 +151,7 @@ main() {
cp -r $OUTPUT_DIRECTORY ./build/chrome-enterprise
cp -r $OUTPUT_DIRECTORY ./build/chrome-consumer
cp -r $OUTPUT_DIRECTORY ./build/firefox-consumer
cp -r $OUTPUT_DIRECTORY ./build/thunderbird-consumer
node ./build/tooling/build-types-and-manifests
}

Expand Down
2 changes: 1 addition & 1 deletion test/source/patterns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ for (const buildType of ['chrome-consumer', 'chrome-enterprise']) {
}
for (const expectedPermission of expectedPermissions) {
if (!manifest.permissions.includes(expectedPermission)) {
if (!(expectedPermission === 'unlimitedStorage' && buildType === 'firefox-consumer')) {
if (!(expectedPermission === 'unlimitedStorage' && (buildType === 'firefox-consumer' || buildType === 'thunderbird-consumer'))) {
console.error(`Missing permission '${expectedPermission}' in ${buildType}/manifest.json`);
errsFound++;
}
Expand Down
27 changes: 25 additions & 2 deletions tooling/build-types-and-manifests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ const DIR = './build';
const version: string = JSON.parse(readFileSync('./package.json').toString()).version;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const addManifest = (toBuildType: string, transform: (manifest: { [k: string]: any }) => void) => {
const manifest = JSON.parse(readFileSync(`${DIR}/generic-extension-wip/manifest.json`).toString());
const addManifest = (toBuildType: string, transform: (manifest: { [k: string]: any }) => void, fromBuildType = 'generic-extension-wip') => {
const manifest = JSON.parse(readFileSync(`${DIR}/${fromBuildType}/manifest.json`).toString());
transform(manifest);
writeFileSync(`${DIR}/${toBuildType}/manifest.json`, JSON.stringify(manifest, undefined, 2));
};
Expand Down Expand Up @@ -49,6 +49,29 @@ addManifest('firefox-consumer', manifest => {
delete manifest.minimum_chrome_version;
});

addManifest(
'thunderbird-consumer',
manifest => {
manifest.browser_specific_settings.strict_min_version = '102.0';
manifest.browser_action.default_title = 'FlowCrypt Encryption for Thunderbird';
manifest.name = 'FlowCrypt Encryption for Thunderbird';
manifest.description = 'Secure end-to-end encryption with FlowCrypt'; // needs to updated later
manifest.compose_action = {
default_title: 'FlowCrypt', // eslint-disable-line @typescript-eslint/naming-convention
default_icon: '/img/logo/flowcrypt-logo-64-64.png', // eslint-disable-line @typescript-eslint/naming-convention
// default_popup will be updated later
default_popup: '/chrome/popups/default.htm', // eslint-disable-line @typescript-eslint/naming-convention
};
manifest.message_display_action = {
default_title: 'FlowCrypt', // eslint-disable-line @typescript-eslint/naming-convention
default_icon: '/img/logo/flowcrypt-logo-64-64.png', // eslint-disable-line @typescript-eslint/naming-convention
// default_popup will be updated later
default_popup: '/chrome/popups/default.htm', // eslint-disable-line @typescript-eslint/naming-convention
};
},
'firefox-consumer'
);

addManifest('chrome-enterprise', manifest => {
manifest.version = version;
manifest.name = 'FlowCrypt for Enterprise';
Expand Down
Loading