diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json
index be4275d8bb5..ec562879dfe 100644
--- a/src/_locales/en/messages.json
+++ b/src/_locales/en/messages.json
@@ -1371,5 +1371,17 @@
},
"desktopIntegrationVerificationText": {
"message": "Please verify that the desktop application shows this fingerprint: "
+ },
+ "desktopIntegrationDisabledTitle": {
+ "message": "Browser integration is not enabled"
+ },
+ "desktopIntegrationDisabledDesc": {
+ "message": "Browser integration is not enabled in the Bitwarden Desktop Application. Please enable it in the settings within the desktop application."
+ },
+ "startDesktopTitle": {
+ "message": "Start the Bitwarden Desktop Application"
+ },
+ "startDesktopDesc": {
+ "message": "The bitwarden desktop application needs to be started before this function can be used."
}
}
diff --git a/src/background/nativeMessaging.background.ts b/src/background/nativeMessaging.background.ts
index caebdc8e277..69389358702 100644
--- a/src/background/nativeMessaging.background.ts
+++ b/src/background/nativeMessaging.background.ts
@@ -16,6 +16,7 @@ const EncryptionAlgorithm = 'sha1';
export class NativeMessagingBackground {
private connected = false;
+ private connecting: boolean;
private port: browser.runtime.Port | chrome.runtime.Port;
private resolver: any = null;
@@ -29,22 +30,54 @@ export class NativeMessagingBackground {
private runtimeBackground: RuntimeBackground, private i18nService: I18nService, private userService: UserService,
private messagingService: MessagingService) {}
- connect() {
- this.port = BrowserApi.connectNative('com.8bit.bitwarden');
+ async connect() {
+ return new Promise((resolve, reject) => {
+ this.port = BrowserApi.connectNative('com.8bit.bitwarden');
- this.connected = true;
+ this.connecting = true;
- this.port.onMessage.addListener((msg) => this.onMessage(msg));
+ this.port.onMessage.addListener((message: any) => {
+ if (message.command === 'connected') {
+ this.connected = true;
+ this.connecting = false;
+ resolve();
+ } else if (message.command === 'disconnected') {
+ if (this.connecting) {
+ this.messagingService.send('showDialog', {
+ text: this.i18nService.t('startDesktopDesc'),
+ title: this.i18nService.t('startDesktopTitle'),
+ confirmText: this.i18nService.t('ok'),
+ type: 'error',
+ });
+ reject();
+ }
+ this.connected = false;
+ this.port.disconnect();
+ return;
+ }
- this.port.onDisconnect.addListener(() => {
- this.connected = false;
+ this.onMessage(message);
+ });
+
+ this.port.onDisconnect.addListener(() => {
+ if (BrowserApi.runtimeLastError().message === 'Specified native messaging host not found.') {
+ this.messagingService.send('showDialog', {
+ text: this.i18nService.t('desktopIntegrationDisabledDesc'),
+ title: this.i18nService.t('desktopIntegrationDisabledTitle'),
+ confirmText: this.i18nService.t('ok'),
+ type: 'error',
+ });
+ }
+ this.connected = false;
+ reject();
+ });
});
}
async send(message: any) {
// If not connected, try to connect
if (!this.connected) {
- this.connect();
+ await this.connect();
}
if (this.sharedSecret == null) {
@@ -118,7 +151,7 @@ export class NativeMessagingBackground {
const fingerprint = (await this.cryptoService.getFingerprint(await this.userService.getUserId(), this.publicKey)).join(' ');
this.messagingService.send('showDialog', {
- html: `${this.i18nService.t('desktopIntegrationVerificationText')}
${fingerprint}.`,
+ html: `${this.i18nService.t('desktopIntegrationVerificationText')}
${fingerprint}`,
title: this.i18nService.t('desktopSyncVerificationTitle'),
confirmText: this.i18nService.t('ok'),
type: 'warning',
@@ -129,7 +162,7 @@ export class NativeMessagingBackground {
private async sendUnencrypted(message: any) {
if (!this.connected) {
- this.connect();
+ await this.connect();
}
message.timestamp = Date.now();
diff --git a/src/browser/browserApi.ts b/src/browser/browserApi.ts
index 1ee54a45b18..660defcca33 100644
--- a/src/browser/browserApi.ts
+++ b/src/browser/browserApi.ts
@@ -229,4 +229,12 @@ export class BrowserApi {
return chrome.runtime.connectNative(application);
}
}
+
+ static runtimeLastError(): browser.runtime._LastError | chrome.runtime.LastError {
+ if (BrowserApi.isWebExtensionsApi) {
+ return browser.runtime.lastError;
+ } else if (BrowserApi.isChromeApi) {
+ return chrome.runtime.lastError;
+ }
+ }
}