Skip to content

Commit

Permalink
issue #2504 Move most of openExtensionTabs from BgUtils to Browser (#…
Browse files Browse the repository at this point in the history
…2714)

* Move openExtensionTab from BgUtils to Browser

* use chrome.tabs.create in bg scripts

* giving up on making window.open() work in Firefox

* Revert "giving up on making window.open() work in Firefox"

This reverts commit b2de265.

* bring back BrowserMsg.send.bg.settings

* use BrowserMsg.send.bg.settings() in setup-webmail-content

window.open() doesn't work for opening extensions' links from Gmail UI

* bring back BgUtils.openSettingsPage to bg scripts

* add missing async/await

* use BrowserMsg.send.bg.settings in gmail-element-replacer

* make Browser.openExtensionTab private

* mode chrome.tabs from BgUtis to Browser

* remove forgotten testing line

* Revert "mode chrome.tabs from BgUtis to Browser"

This reverts commit c77e251.
  • Loading branch information
limonte committed Apr 29, 2020
1 parent a9dd3c5 commit def2eba
Show file tree
Hide file tree
Showing 18 changed files with 57 additions and 39 deletions.
3 changes: 2 additions & 1 deletion extension/chrome/elements/add_pubkey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { KeyImportUi, UserAlert, } from '../../js/common/ui/key-import-ui.js';

import { Assert } from '../../js/common/assert.js';
import { AttUI } from '../../js/common/ui/att-ui.js';
import { Browser } from '../../js/common/browser/browser.js';
import { BrowserMsg } from '../../js/common/browser/browser-msg.js';
import { Catch } from '../../js/common/platform/catch.js';
import { FetchKeyUI } from '../../js/common/ui/fetch-key-ui.js';
Expand Down Expand Up @@ -40,7 +41,7 @@ View.run(class AddPubkeyView extends View {
Xss.sanitizeAppend('select.copy_from_email', `<option value="${Xss.escape(contact.email)}">${Xss.escape(contact.email)}</option>`);
}
this.fetchKeyUi.handleOnPaste($('.pubkey'));
$('.action_settings').click(this.setHandler(() => BrowserMsg.send.bg.settings({ path: 'index.htm', page: '/chrome/settings/modules/contacts.htm', acctEmail: this.acctEmail })));
$('.action_settings').click(this.setHandler(async () => await Browser.openSettingsPage('index.htm', this.acctEmail, '/chrome/settings/modules/contacts.htm')));
}

public setHandlers = () => {
Expand Down
3 changes: 2 additions & 1 deletion extension/chrome/elements/backup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
'use strict';

import { Assert } from '../../js/common/assert.js';
import { Browser } from '../../js/common/browser/browser.js';
import { BrowserMsg } from '../../js/common/browser/browser-msg.js';
import { KeyInfo } from '../../js/common/core/pgp-key.js';
import { PgpKey } from '../../js/common/core/pgp-key.js';
Expand Down Expand Up @@ -60,7 +61,7 @@ View.run(class BackupView extends View {

public setHandlers = () => {
if (!this.storedPrvWithMatchingLongid) {
$("#action_import_key").click(this.setHandler(() => BrowserMsg.send.bg.settings({ acctEmail: this.acctEmail, page: '/chrome/settings/modules/add_key.htm' })));
$("#action_import_key").click(this.setHandler(async () => await Browser.openSettingsPage('index.htm', this.acctEmail, '/chrome/settings/modules/add_key.htm')));
}
$('.action_test_pass').click(this.setHandler(async () => this.testPassphraseHandler()));
$('#pass_phrase').keydown(this.setEnterHandlerThatClicks('.action_test_pass'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

'use strict';

import { Browser } from '../../../js/common/browser/browser.js';
import { BrowserEventErrHandler, Ui } from '../../../js/common/browser/ui.js';
import { Catch } from '../../../js/common/platform/catch.js';
import { NewMsgData, SendBtnTexts } from './compose-types.js';
Expand Down Expand Up @@ -81,7 +82,7 @@ export class ComposeErrModule extends ViewModule<ComposeView> {
if (await Ui.modal.confirm(`Google returned an error when sending message. Please help us improve FlowCrypt by reporting the error to us.`)) {
const page = '/chrome/settings/modules/help.htm';
const pageUrlParams = { bugReport: BrowserExtension.prepareBugReport(`composer: send: bad request (errMsg: ${e.parsedErrMsg})`, {}, e) };
BrowserMsg.send.bg.settings({ acctEmail: this.view.acctEmail, page, pageUrlParams });
await Browser.openSettingsPage('index.htm', this.view.acctEmail, page, pageUrlParams);
}
}
} else if (e instanceof ComposerUserError) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ export class ComposePwdOrPubkeyContainerModule extends ViewModule<ComposeView> {
this.view.S.cached('input_password').keyup(this.view.setHandlerPrevent('spree', () => this.showHideContainerAndColorSendBtn()));
this.view.S.cached('input_password').focus(this.view.setHandlerPrevent('spree', () => this.inputPwdFocusHandler()));
this.view.S.cached('input_password').blur(this.view.setHandler(() => this.inputPwdBlurHandler()));
this.view.S.cached('expiration_note').find('#expiration_note_settings_link').click(this.view.setHandler((el, e) => {
this.view.S.cached('expiration_note').find('#expiration_note_settings_link').click(this.view.setHandler(async (el, e) => {
e.preventDefault();
this.view.renderModule.renderSettingsWithDialog('security');
await this.view.renderModule.openSettingsWithDialog('security');
}, this.view.errModule.handle(`render settings dialog`)));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
'use strict';

import { Att } from '../../../js/common/core/att.js';
import { Browser } from '../../../js/common/browser/browser.js';
import { BrowserMsg } from '../../../js/common/browser/browser-msg.js';
import { Catch } from '../../../js/common/platform/catch.js';
import { KeyImportUi } from '../../../js/common/ui/key-import-ui.js';
Expand Down Expand Up @@ -55,7 +56,7 @@ export class ComposeRenderModule extends ViewModule<ComposeView> {
Alternatively, <a href="#" class="new_message_button">compose a new secure message</a> to respond.<br/><br/>
`);
this.view.S.cached('prompt').attr('style', 'border:none !important');
$('.auth_settings').click(() => BrowserMsg.send.bg.settings({ acctEmail: this.view.acctEmail, page: '/chrome/settings/modules/auth_denied.htm' }));
$('.auth_settings').click(async () => await Browser.openSettingsPage('index.htm', this.view.acctEmail, '/chrome/settings/modules/auth_denied.htm'));
$('.new_message_button').click(() => BrowserMsg.send.openNewMessage(this.view.parentTabId));
}
this.view.sizeModule.resizeComposeBox();
Expand Down Expand Up @@ -101,8 +102,8 @@ export class ComposeRenderModule extends ViewModule<ComposeView> {
}
}

public renderSettingsWithDialog = (settingsModule: string) => {
BrowserMsg.send.bg.settings({ acctEmail: this.view.acctEmail, page: `/chrome/settings/modules/${settingsModule}.htm` });
public openSettingsWithDialog = async (settingsModule: string) => {
await Browser.openSettingsPage('index.htm', this.view.acctEmail, `/chrome/settings/modules/${settingsModule}.htm`);
}

public initComposeBox = async () => {
Expand Down
2 changes: 1 addition & 1 deletion extension/chrome/elements/compose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export class ComposeView extends View {
}

public setHandlers = () => {
this.S.cached('icon_help').click(this.setHandler(() => this.renderModule.renderSettingsWithDialog('help'), this.errModule.handle(`help dialog`)));
this.S.cached('icon_help').click(this.setHandler(async () => await this.renderModule.openSettingsWithDialog('help'), this.errModule.handle(`help dialog`)));
this.attsModule.setHandlers();
this.inputModule.setHandlers();
this.myPubkeyModule.setHandlers();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

'use strict';

import { Browser } from '../../../js/common/browser/browser.js';
import { BrowserMsg } from '../../../js/common/browser/browser-msg.js';
import { Buf } from '../../../js/common/core/buf.js';
import { DecryptErrTypes } from '../../../js/common/core/pgp-msg.js';
Expand Down Expand Up @@ -60,7 +61,7 @@ export class PgpBlockViewDecryptModule {
<button class="button green auth_settings">Add missing permission</button>`;
Xss.sanitizeRender('#pgp_block', `This encrypted message is very large (possibly containing an attachment). ${readAccess}`);
this.view.renderModule.resizePgpBlockFrame();
$('.auth_settings').click(this.view.setHandler(() => BrowserMsg.send.bg.settings({ acctEmail: this.view.acctEmail, page: '/chrome/settings/modules/auth_denied.htm' })));
$('.auth_settings').click(this.view.setHandler(async () => await Browser.openSettingsPage('index.htm', this.view.acctEmail, '/chrome/settings/modules/auth_denied.htm')));
}
}
} catch (e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
'use strict';

import { ApiErr } from '../../../js/common/api/error/api-error.js';
import { Browser } from '../../../js/common/browser/browser.js';
import { BrowserMsg } from '../../../js/common/browser/browser-msg.js';
import { Catch } from '../../../js/common/platform/catch.js';
import { FormatError } from '../../../js/common/core/pgp-msg.js';
Expand All @@ -24,9 +25,9 @@ export class PgpBlockViewErrorModule {
$('.action_show_raw_pgp_block').click(this.view.setHandler(async () => { // this may contain content missing MDC
Xss.sanitizeAppend('#pgp_block', `<div class="raw_pgp_block">${Xss.escape(renderRawMsg!)}</div>`); // therefore the .escape is crucial
}));
$('.button.settings_keyserver').click(this.view.setHandler(() => BrowserMsg.send.bg.settings({ acctEmail: this.view.acctEmail, page: '/chrome/settings/modules/keyserver.htm' })));
$('.button.settings').click(this.view.setHandler(() => BrowserMsg.send.bg.settings({ acctEmail: this.view.acctEmail })));
$('.button.settings_add_key').click(this.view.setHandler(() => BrowserMsg.send.bg.settings({ acctEmail: this.view.acctEmail, page: '/chrome/settings/modules/add_key.htm' })));
$('.button.settings_keyserver').click(this.view.setHandler(async () => await Browser.openSettingsPage('index.htm', this.view.acctEmail, '/chrome/settings/modules/keyserver.htm')));
$('.button.settings').click(this.view.setHandler(async () => await Browser.openSettingsPage('index.htm', this.view.acctEmail)));
$('.button.settings_add_key').click(this.view.setHandler(async () => await Browser.openSettingsPage('index.htm', this.view.acctEmail, '/chrome/settings/modules/add_key.htm')));
$('.button.reply_pubkey_mismatch').click(this.view.setHandler(() => BrowserMsg.send.replyPubkeyMismatch(this.view.parentTabId)));
Ui.setTestState('ready');
}
Expand Down
5 changes: 3 additions & 2 deletions extension/chrome/popups/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

'use strict';

import { Browser } from '../../js/common/browser/browser.js';
import { BrowserMsg } from '../../js/common/browser/browser-msg.js';
import { Catch } from '../../js/common/platform/catch.js';
import { Ui } from '../../js/common/browser/ui.js';
Expand Down Expand Up @@ -50,7 +51,7 @@ View.run(class DefaultPopupView extends View {
}

private redirectToInitSetup = async (acctEmail?: string) => {
BrowserMsg.send.bg.settings({ acctEmail: acctEmail || undefined });
await Browser.openSettingsPage('index.htm', acctEmail || undefined);
await Ui.time.sleep(100);
window.close();
}
Expand All @@ -66,7 +67,7 @@ View.run(class DefaultPopupView extends View {
}));
$('.action_open_encrypted_inbox').click(this.setHandler(async () => {
if (activeAcctEmail) {
BrowserMsg.send.bg.inbox({ acctEmail: activeAcctEmail });
await Browser.openSettingsPage('inbox/inbox.htm', activeAcctEmail);
await Ui.time.sleep(100);
window.close();
} else {
Expand Down
14 changes: 6 additions & 8 deletions extension/chrome/popups/select_account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
'use strict';

import { Assert } from '../../js/common/assert.js';
import { BrowserMsg } from '../../js/common/browser/browser-msg.js';
import { Browser } from '../../js/common/browser/browser.js';
import { Catch } from '../../js/common/platform/catch.js';
import { Ui } from '../../js/common/browser/ui.js';
import { Url } from '../../js/common/core/common.js';
Expand Down Expand Up @@ -47,18 +47,16 @@ View.run(class SelectAcctPopupView extends View {

private actionChooseAcctHandler = async (clickedElement: HTMLElement) => {
if (this.action === 'inbox') {
BrowserMsg.send.bg.inbox({ acctEmail: $(clickedElement).attr('email') });
await Ui.time.sleep(100);
window.close();
await Browser.openSettingsPage('inbox/inbox.htm', $(clickedElement).attr('email'));
} else {
BrowserMsg.send.bg.settings({ acctEmail: $(clickedElement).attr('email') });
await Ui.time.sleep(100);
window.close();
await Browser.openSettingsPage('index.htm', $(clickedElement).attr('email'));
}
await Ui.time.sleep(100);
window.close();
}

private actionRedirectToAddAcctPageHandler = async () => {
BrowserMsg.send.bg.settings({ addNewAcct: true });
await Browser.openSettingsPage('index.htm', undefined, undefined, undefined, true);
await Ui.time.sleep(100);
window.close();
}
Expand Down
3 changes: 2 additions & 1 deletion extension/chrome/settings/inbox/inbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { SelCache, Ui } from '../../../js/common/browser/ui.js';
import { Url, UrlParams } from '../../../js/common/core/common.js';
import { ApiErr } from '../../../js/common/api/error/api-error.js';
import { Assert } from '../../../js/common/assert.js';
import { Browser } from '../../../js/common/browser/browser.js';
import { BrowserMsg, Bm } from '../../../js/common/browser/browser-msg.js';
import { Catch } from '../../../js/common/platform/catch.js';
import { Gmail } from '../../../js/common/api/email-provider/gmail/gmail.js';
Expand Down Expand Up @@ -91,7 +92,7 @@ export class InboxView extends View {
// BrowserMsg.addPgpListeners(); // todo - re-allow when https://github.com/FlowCrypt/flowcrypt-browser/issues/2560 fixed
BrowserMsg.listen(this.tabId);
Catch.setHandledInterval(this.webmailCommon.addOrRemoveEndSessionBtnIfNeeded, 30000);
$('.action_open_settings').click(this.setHandler(self => BrowserMsg.send.bg.settings({ acctEmail: this.acctEmail })));
$('.action_open_settings').click(this.setHandler(async () => await Browser.openSettingsPage('index.htm', this.acctEmail)));
$(".action-toggle-accounts-menu").click(this.setHandler((target, event) => {
event.stopPropagation();
$("#alt-accounts").toggleClass("active");
Expand Down
9 changes: 5 additions & 4 deletions extension/chrome/settings/modules/backup-status-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Xss } from '../../../js/common/platform/xss.js';
import { BackupView } from './backup.js';
import { Ui } from '../../../js/common/browser/ui.js';
import { ApiErr } from '../../../js/common/api/error/api-error.js';
import { Browser } from '../../../js/common/browser/browser.js';
import { BrowserMsg } from '../../../js/common/browser/browser-msg.js';
import { Backups } from '../../../js/common/api/email-provider/email-provider-api.js';
import { KeyInfo } from '../../../js/common/core/pgp-key.js';
Expand All @@ -16,8 +17,8 @@ export class BackupStatusModule extends ViewModule<BackupView> {

public setHandlers = () => { // is run after checkAndRenderBackupStatus, which renders (some of) these fields first
$('#module_status .action_go_manual').click(this.view.setHandler(el => this.actionShowManualBackupHandler()));
$('#module_status .action_go_auth_denied').click(this.view.setHandler(() => this.goTo('auth_denied.htm')));
$('#module_status .action_go_add_key').click(this.view.setHandler(() => this.goTo('add_key.htm')));
$('#module_status .action_go_auth_denied').click(this.view.setHandler(async () => await this.goTo('auth_denied.htm')));
$('#module_status .action_go_add_key').click(this.view.setHandler(async () => await this.goTo('add_key.htm')));
}

public checkAndRenderBackupStatus = async () => {
Expand Down Expand Up @@ -85,8 +86,8 @@ export class BackupStatusModule extends ViewModule<BackupView> {
$('h1').text('Back up your private key');
}

private goTo = (page: string) => {
BrowserMsg.send.bg.settings({ acctEmail: this.view.acctEmail, page: `/chrome/settings/modules/${page}` });
private goTo = async (page: string) => {
await Browser.openSettingsPage('index.htm', this.view.acctEmail, `/chrome/settings/modules/${page}`);
}

}
2 changes: 1 addition & 1 deletion extension/chrome/settings/modules/experimental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ View.run(class ExperimentalView extends View {
try {
await Settings.acctStorageChangeEmail(this.acctEmail, response.acctEmail);
await Ui.modal.info(`Email address changed to ${response.acctEmail}. You should now check that your public key is properly submitted.`);
BrowserMsg.send.bg.settings({ path: 'index.htm', page: '/chrome/settings/modules/keyserver.htm', acctEmail: response.acctEmail });
await Browser.openSettingsPage('index.htm', response.acctEmail, '/chrome/settings/modules/keyserver.htm');
} catch (e) {
Catch.reportErr(e);
await Ui.modal.error('There was an error changing google account, please write human@flowcrypt.com');
Expand Down
1 change: 0 additions & 1 deletion extension/js/background_page/background_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ opgp.initWorker({ path: '/lib/openpgp.worker.js' });
BrowserMsg.bgAddListener('ajax', BgHandlers.ajaxHandler);
BrowserMsg.bgAddListener('ajaxGmailAttGetChunk', BgHandlers.ajaxGmailAttGetChunkHandler);
BrowserMsg.bgAddListener('settings', BgHandlers.openSettingsPageHandler);
BrowserMsg.bgAddListener('inbox', BgHandlers.openInboxPageHandler);
BrowserMsg.bgAddListener('update_uninstall_url', BgHandlers.updateUninstallUrl);
BrowserMsg.bgAddListener('get_active_tab_info', BgHandlers.getActiveTabInfo);
BrowserMsg.bgAddListener('reconnect_acct_auth_popup', (r: Bm.ReconnectAcctAuthPopup) => GoogleAuth.newAuthPopup(r));
Expand Down
5 changes: 0 additions & 5 deletions extension/js/background_page/bg-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { Api } from '../common/api/api.js';
import { BgUtils } from './bgutils.js';
import { Bm } from '../common/browser/browser-msg.js';
import { Gmail } from '../common/api/email-provider/gmail/gmail.js';
import { Url } from '../common/core/common.js';
import { GlobalStore } from '../common/platform/store/global-store.js';
import { ContactStore } from '../common/platform/store/contact-store.js';

Expand All @@ -16,10 +15,6 @@ export class BgHandlers {
await BgUtils.openSettingsPage(path, acctEmail, page, pageUrlParams, addNewAcct === true);
}

public static openInboxPageHandler: Bm.AsyncResponselessHandler = async (message: { acctEmail: string, threadId?: string, folder?: string }) => {
await BgUtils.openExtensionTab(Url.create(chrome.runtime.getURL(`chrome/settings/inbox/inbox.htm`), message));
}

public static dbOperationHandler = async (db: IDBDatabase, request: Bm.Db): Promise<Bm.Res.Db> => {
if (!db) {
console.info(`db corrupted, skipping: ${request.f}`);
Expand Down
4 changes: 2 additions & 2 deletions extension/js/common/assert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { Catch, UnreportableError } from './platform/catch.js';
import { Dict, UrlParam, UrlParams } from './core/common.js';
import { BrowserMsg } from './browser/browser-msg.js';
import { Browser } from './browser/browser.js';
import { KeyInfo } from './core/pgp-key.js';
import { PgpKey } from './core/pgp-key.js';
import { Settings } from './settings.js';
Expand Down Expand Up @@ -45,7 +45,7 @@ export class Assert {
const msg = `Protect your key with a pass phrase to finish setup.`;
const r = await Ui.renderOverlayPromptAwaitUserChoice({ finishSetup: {}, later: { color: 'gray' } }, msg);
if (r === 'finish_setup') {
BrowserMsg.send.bg.settings({ acctEmail });
await Browser.openSettingsPage('index.htm', acctEmail);
}
}
}
Expand Down
2 changes: 0 additions & 2 deletions extension/js/common/browser/browser-msg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ export namespace Bm {
export type StoreGlobalSet = { values: GlobalStoreDict; };
export type StoreAcctGet = { acctEmail: string, keys: AccountIndex[]; };
export type StoreAcctSet = { acctEmail: string, values: AcctStoreDict; };
export type Inbox = { acctEmail?: string };
export type ReconnectAcctAuthPopup = { acctEmail: string, scopes?: string[] };
export type PgpKeyDetails = { pubkey: string };
export type PgpMsgDecrypt = PgpMsgMethod.Arg.Decrypt;
Expand Down Expand Up @@ -123,7 +122,6 @@ export class BrowserMsg {
bg: {
settings: (bm: Bm.Settings) => BrowserMsg.sendCatch(undefined, 'settings', bm),
updateUninstallUrl: () => BrowserMsg.sendCatch(undefined, 'update_uninstall_url', {}),
inbox: (bm: Bm.Inbox) => BrowserMsg.sendCatch(undefined, 'inbox', bm),
await: {
reconnectAcctAuthPopup: (bm: Bm.ReconnectAcctAuthPopup) => BrowserMsg.sendAwait(undefined, 'reconnect_acct_auth_popup', bm, true) as Promise<Bm.Res.ReconnectAcctAuthPopup>,
getActiveTabInfo: () => BrowserMsg.sendAwait(undefined, 'get_active_tab_info', undefined, true) as Promise<Bm.Res.GetActiveTabInfo>,
Expand Down
19 changes: 19 additions & 0 deletions extension/js/common/browser/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import { Api } from '../api/api.js';
import { Att } from '../core/att.js';
import { Catch } from '../platform/catch.js';
import { Dict, Url, UrlParam } from '../core/common.js';
import { GlobalStore } from '../platform/store/global-store.js';
import { Ui } from './ui.js';
import { Xss } from '../platform/xss.js';

Expand Down Expand Up @@ -76,4 +78,21 @@ export class Browser {
return array;
}

public static openSettingsPage = async (path: string = 'index.htm', acctEmail?: string, page: string = '', rawPageUrlParams?: Dict<UrlParam>, addNewAcct = false) => {
const basePath = chrome.runtime.getURL(`chrome/settings/${path}`);
const pageUrlParams = rawPageUrlParams ? JSON.stringify(rawPageUrlParams) : undefined;
if (acctEmail || path === 'fatal.htm') {
await Browser.openExtensionTab(Url.create(basePath, { acctEmail, page, pageUrlParams }));
} else if (addNewAcct) {
await Browser.openExtensionTab(Url.create(basePath, { addNewAcct }));
} else {
const acctEmails = await GlobalStore.acctEmailsGet();
await Browser.openExtensionTab(Url.create(basePath, { acctEmail: acctEmails[0], page, pageUrlParams }));
}
}

private static openExtensionTab = async (url: string) => {
window.open(url, 'flowcrypt');
}

}

0 comments on commit def2eba

Please sign in to comment.