From 5d12aef6db5f9d576c687e1189bdc7e2f952e4b9 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 6 Nov 2020 10:32:37 +0100 Subject: [PATCH 01/13] support pre-sharing room keys in room encryption --- src/matrix/e2ee/RoomEncryption.js | 25 ++++++++++-- src/matrix/e2ee/megolm/Encryption.js | 61 +++++++++++++++++++--------- 2 files changed, 63 insertions(+), 23 deletions(-) diff --git a/src/matrix/e2ee/RoomEncryption.js b/src/matrix/e2ee/RoomEncryption.js index 90c63b4a..7d60a475 100644 --- a/src/matrix/e2ee/RoomEncryption.js +++ b/src/matrix/e2ee/RoomEncryption.js @@ -244,7 +244,26 @@ export class RoomEncryption { } return matches; - + } + + /** shares the encryption key for the next message if needed */ + async ensureNextMessageEncryptionKeyIsShared(hsApi) { + const txn = this._storage.readWriteTxn([ + this._storage.storeNames.operations, + this._storage.storeNames.outboundGroupSessions, + this._storage.storeNames.inboundGroupSessions, + ]); + let roomKeyMessage; + try { + roomKeyMessage = await this._megolmEncryption.ensureOutboundSession(this._room.id, this._encryptionParams, txn); + } catch (err) { + txn.abort(); + throw err; + } + // will complete the txn + if (roomKeyMessage) { + await this._shareNewRoomKey(roomKeyMessage, hsApi, txn); + } } async encrypt(type, content, hsApi) { @@ -269,12 +288,12 @@ export class RoomEncryption { return false; } - async _shareNewRoomKey(roomKeyMessage, hsApi) { + async _shareNewRoomKey(roomKeyMessage, hsApi, txn = null) { const devices = await this._deviceTracker.devicesForTrackedRoom(this._room.id, hsApi); const userIds = Array.from(devices.reduce((set, device) => set.add(device.userId), new Set())); // store operation for room key share, in case we don't finish here - const writeOpTxn = this._storage.readWriteTxn([this._storage.storeNames.operations]); + const writeOpTxn = txn || this._storage.readWriteTxn([this._storage.storeNames.operations]); let operationId; try { operationId = this._writeRoomKeyShareOperation(roomKeyMessage, userIds, writeOpTxn); diff --git a/src/matrix/e2ee/megolm/Encryption.js b/src/matrix/e2ee/megolm/Encryption.js index a0769ba1..a1257199 100644 --- a/src/matrix/e2ee/megolm/Encryption.js +++ b/src/matrix/e2ee/megolm/Encryption.js @@ -43,6 +43,45 @@ export class Encryption { } } + async ensureOutboundSession(roomId, encryptionParams, txn) { + let session = new this._olm.OutboundGroupSession(); + try { + let sessionEntry = await txn.outboundGroupSessions.get(roomId); + const roomKeyMessage = this._readOrCreateSession(session, sessionEntry, roomId, encryptionParams, txn); + if (roomKeyMessage) { + this._writeSession(sessionEntry, session, roomId, txn); + return roomKeyMessage; + } + } finally { + session.free(); + } + } + + _readOrCreateSession(session, sessionEntry, roomId, encryptionParams, txn) { + if (sessionEntry) { + session.unpickle(this._pickleKey, sessionEntry.session); + } + if (!sessionEntry || this._needsToRotate(session, sessionEntry.createdAt, encryptionParams)) { + // in the case of rotating, recreate a session as we already unpickled into it + if (sessionEntry) { + session.free(); + session = new this._olm.OutboundGroupSession(); + } + session.create(); + const roomKeyMessage = this._createRoomKeyMessage(session, roomId); + this._storeAsInboundSession(session, roomId, txn); + return roomKeyMessage; + } + } + + _writeSession(sessionEntry, session, roomId, txn) { + txn.outboundGroupSessions.set({ + roomId, + session: session.pickle(this._pickleKey), + createdAt: sessionEntry?.createdAt || this._now(), + }); + } + /** * Encrypts a message with megolm * @param {string} roomId @@ -61,28 +100,10 @@ export class Encryption { let roomKeyMessage; let encryptedContent; try { - // TODO: we could consider keeping the session in memory for the current room let sessionEntry = await txn.outboundGroupSessions.get(roomId); - if (sessionEntry) { - session.unpickle(this._pickleKey, sessionEntry.session); - } - if (!sessionEntry || this._needsToRotate(session, sessionEntry.createdAt, encryptionParams)) { - // in the case of rotating, recreate a session as we already unpickled into it - if (sessionEntry) { - session.free(); - session = new this._olm.OutboundGroupSession(); - } - session.create(); - roomKeyMessage = this._createRoomKeyMessage(session, roomId); - this._storeAsInboundSession(session, roomId, txn); - // TODO: we could tell the Decryption here that we have a new session so it can add it to its cache - } + roomKeyMessage = this._readOrCreateSession(session, sessionEntry, roomId, encryptionParams, txn); encryptedContent = this._encryptContent(roomId, session, type, content); - txn.outboundGroupSessions.set({ - roomId, - session: session.pickle(this._pickleKey), - createdAt: sessionEntry?.createdAt || this._now(), - }); + this._writeSession(sessionEntry, session, roomId, txn); } catch (err) { txn.abort(); From d0d1f68a9cbc3f3700cc1ff0a47a37521cf6fedd Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 6 Nov 2020 16:56:12 +0100 Subject: [PATCH 02/13] WIP --- src/matrix/e2ee/megolm/Encryption.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/matrix/e2ee/megolm/Encryption.js b/src/matrix/e2ee/megolm/Encryption.js index a1257199..fea9cd29 100644 --- a/src/matrix/e2ee/megolm/Encryption.js +++ b/src/matrix/e2ee/megolm/Encryption.js @@ -43,15 +43,27 @@ export class Encryption { } } - async ensureOutboundSession(roomId, encryptionParams, txn) { + async ensureOutboundSession(roomId, encryptionParams) { let session = new this._olm.OutboundGroupSession(); try { - let sessionEntry = await txn.outboundGroupSessions.get(roomId); - const roomKeyMessage = this._readOrCreateSession(session, sessionEntry, roomId, encryptionParams, txn); - if (roomKeyMessage) { - this._writeSession(sessionEntry, session, roomId, txn); - return roomKeyMessage; + const txn = this._storage.readWriteTxn([ + this._storage.storeNames.inboundGroupSessions, + this._storage.storeNames.outboundGroupSessions, + ]); + let roomKeyMessage; + try { + let sessionEntry = await txn.outboundGroupSessions.get(roomId); + roomKeyMessage = this._readOrCreateSession(session, sessionEntry, roomId, encryptionParams, txn); + if (roomKeyMessage) { + this._writeSession(sessionEntry, session, roomId, txn); + return roomKeyMessage; + } + } catch (err) { + txn.abort(); + throw err; } + await txn.complete(); + return roomKeyMessage; } finally { session.free(); } From c6ff4c2517515713a8e8369b90562206a811dc10 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 6 Nov 2020 18:56:32 +0100 Subject: [PATCH 03/13] finish room encryption part --- src/matrix/e2ee/RoomEncryption.js | 22 +++++----------------- src/matrix/e2ee/megolm/Encryption.js | 1 - 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/matrix/e2ee/RoomEncryption.js b/src/matrix/e2ee/RoomEncryption.js index 7d60a475..f10e3f07 100644 --- a/src/matrix/e2ee/RoomEncryption.js +++ b/src/matrix/e2ee/RoomEncryption.js @@ -247,22 +247,10 @@ export class RoomEncryption { } /** shares the encryption key for the next message if needed */ - async ensureNextMessageEncryptionKeyIsShared(hsApi) { - const txn = this._storage.readWriteTxn([ - this._storage.storeNames.operations, - this._storage.storeNames.outboundGroupSessions, - this._storage.storeNames.inboundGroupSessions, - ]); - let roomKeyMessage; - try { - roomKeyMessage = await this._megolmEncryption.ensureOutboundSession(this._room.id, this._encryptionParams, txn); - } catch (err) { - txn.abort(); - throw err; - } - // will complete the txn + async ensureNextMessageKeyIsShared(hsApi) { + const roomKeyMessage = await this._megolmEncryption.ensureOutboundSession(this._room.id, this._encryptionParams); if (roomKeyMessage) { - await this._shareNewRoomKey(roomKeyMessage, hsApi, txn); + await this._shareNewRoomKey(roomKeyMessage, hsApi); } } @@ -288,12 +276,12 @@ export class RoomEncryption { return false; } - async _shareNewRoomKey(roomKeyMessage, hsApi, txn = null) { + async _shareNewRoomKey(roomKeyMessage, hsApi) { const devices = await this._deviceTracker.devicesForTrackedRoom(this._room.id, hsApi); const userIds = Array.from(devices.reduce((set, device) => set.add(device.userId), new Set())); // store operation for room key share, in case we don't finish here - const writeOpTxn = txn || this._storage.readWriteTxn([this._storage.storeNames.operations]); + const writeOpTxn = this._storage.readWriteTxn([this._storage.storeNames.operations]); let operationId; try { operationId = this._writeRoomKeyShareOperation(roomKeyMessage, userIds, writeOpTxn); diff --git a/src/matrix/e2ee/megolm/Encryption.js b/src/matrix/e2ee/megolm/Encryption.js index fea9cd29..2e077c47 100644 --- a/src/matrix/e2ee/megolm/Encryption.js +++ b/src/matrix/e2ee/megolm/Encryption.js @@ -56,7 +56,6 @@ export class Encryption { roomKeyMessage = this._readOrCreateSession(session, sessionEntry, roomId, encryptionParams, txn); if (roomKeyMessage) { this._writeSession(sessionEntry, session, roomId, txn); - return roomKeyMessage; } } catch (err) { txn.abort(); From 44a2febce9c39d44a7dc409107a3fbacf255cb4e Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 6 Nov 2020 23:43:02 +0100 Subject: [PATCH 04/13] hook it up --- src/domain/session/room/RoomViewModel.js | 13 +++++++++++-- src/matrix/e2ee/RoomEncryption.js | 3 ++- src/matrix/room/Room.js | 4 ++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/domain/session/room/RoomViewModel.js b/src/domain/session/room/RoomViewModel.js index 5aa20a2a..c872ca0f 100644 --- a/src/domain/session/room/RoomViewModel.js +++ b/src/domain/session/room/RoomViewModel.js @@ -169,6 +169,7 @@ class ComposerViewModel extends ViewModel { super(); this._roomVM = roomVM; this._isEmpty = true; + this._ensureKeyPromise = null; } get isEncrypted() { @@ -188,8 +189,16 @@ class ComposerViewModel extends ViewModel { return !this._isEmpty; } - setInput(text) { + async setInput(text) { + const wasEmpty = this._isEmpty; this._isEmpty = text.length === 0; - this.emitChange("canSend"); + if (wasEmpty && !this._isEmpty && !this._ensureKeyPromise) { + this._ensureKeyPromise = this._roomVM._room.ensureMessageKeyIsShared().then(() => { + this._ensureKeyPromise = null; + }); + } + if (wasEmpty !== this._isEmpty) { + this.emitChange("canSend"); + } } } diff --git a/src/matrix/e2ee/RoomEncryption.js b/src/matrix/e2ee/RoomEncryption.js index f10e3f07..94522e60 100644 --- a/src/matrix/e2ee/RoomEncryption.js +++ b/src/matrix/e2ee/RoomEncryption.js @@ -247,9 +247,10 @@ export class RoomEncryption { } /** shares the encryption key for the next message if needed */ - async ensureNextMessageKeyIsShared(hsApi) { + async ensureMessageKeyIsShared(hsApi) { const roomKeyMessage = await this._megolmEncryption.ensureOutboundSession(this._room.id, this._encryptionParams); if (roomKeyMessage) { + await this._deviceTracker.trackRoom(this._room); await this._shareNewRoomKey(roomKeyMessage, hsApi); } } diff --git a/src/matrix/room/Room.js b/src/matrix/room/Room.js index b2d2b635..8427239c 100644 --- a/src/matrix/room/Room.js +++ b/src/matrix/room/Room.js @@ -354,6 +354,10 @@ export class Room extends EventEmitter { return this._sendQueue.enqueueEvent(eventType, content); } + async ensureMessageKeyIsShared() { + return this._roomEncryption?.ensureMessageKeyIsShared(this._hsApi); + } + /** @public */ async loadMemberList() { if (this._memberList) { From 9cfb3c8e9503b939f13771b39e520ce26c24c768 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 9 Nov 2020 16:49:16 +0100 Subject: [PATCH 05/13] only check to pre-share new megolm session every minute --- src/matrix/e2ee/RoomEncryption.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/matrix/e2ee/RoomEncryption.js b/src/matrix/e2ee/RoomEncryption.js index 94522e60..4829a8d7 100644 --- a/src/matrix/e2ee/RoomEncryption.js +++ b/src/matrix/e2ee/RoomEncryption.js @@ -20,6 +20,10 @@ import {mergeMap} from "../../utils/mergeMap.js"; import {makeTxnId} from "../common.js"; const ENCRYPTED_TYPE = "m.room.encrypted"; +// how often ensureMessageKeyIsShared can check if it needs to +// create a new outbound session +// note that encrypt could still create a new session +const MIN_PRESHARE_INTERVAL = 60 * 1000; // 1min function encodeMissingSessionKey(senderKey, sessionId) { return `${senderKey}|${sessionId}`; @@ -55,6 +59,7 @@ export class RoomEncryption { this._clock = clock; this._disposed = false; this._isFlushingRoomKeyShares = false; + this._lastKeyPreShareTime = null; } async enableSessionBackup(sessionBackup) { @@ -248,6 +253,10 @@ export class RoomEncryption { /** shares the encryption key for the next message if needed */ async ensureMessageKeyIsShared(hsApi) { + if (this._lastKeyPreShareTime && this._lastKeyPreShareTime.measure() < MIN_PRESHARE_INTERVAL) { + return; + } + this._lastKeyPreShareTime = this._clock.createMeasure(); const roomKeyMessage = await this._megolmEncryption.ensureOutboundSession(this._room.id, this._encryptionParams); if (roomKeyMessage) { await this._deviceTracker.trackRoom(this._room); From 6572377832867879b2871fca4aff011b76e917e5 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 9 Nov 2020 16:50:39 +0100 Subject: [PATCH 06/13] move tracking the room to where we need the devices --- src/matrix/e2ee/RoomEncryption.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/matrix/e2ee/RoomEncryption.js b/src/matrix/e2ee/RoomEncryption.js index 4829a8d7..7a177f06 100644 --- a/src/matrix/e2ee/RoomEncryption.js +++ b/src/matrix/e2ee/RoomEncryption.js @@ -259,13 +259,11 @@ export class RoomEncryption { this._lastKeyPreShareTime = this._clock.createMeasure(); const roomKeyMessage = await this._megolmEncryption.ensureOutboundSession(this._room.id, this._encryptionParams); if (roomKeyMessage) { - await this._deviceTracker.trackRoom(this._room); await this._shareNewRoomKey(roomKeyMessage, hsApi); } } async encrypt(type, content, hsApi) { - await this._deviceTracker.trackRoom(this._room); const megolmResult = await this._megolmEncryption.encrypt(this._room.id, type, content, this._encryptionParams); if (megolmResult.roomKeyMessage) { // TODO: should we await this?? @@ -287,6 +285,7 @@ export class RoomEncryption { } async _shareNewRoomKey(roomKeyMessage, hsApi) { + await this._deviceTracker.trackRoom(this._room); const devices = await this._deviceTracker.devicesForTrackedRoom(this._room.id, hsApi); const userIds = Array.from(devices.reduce((set, device) => set.add(device.userId), new Set())); From bd5771e44974be6c181ed4f17578650ff83ece17 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 9 Nov 2020 17:22:37 +0100 Subject: [PATCH 07/13] remove obsolete comment --- src/matrix/e2ee/RoomEncryption.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/matrix/e2ee/RoomEncryption.js b/src/matrix/e2ee/RoomEncryption.js index 7a177f06..86c07606 100644 --- a/src/matrix/e2ee/RoomEncryption.js +++ b/src/matrix/e2ee/RoomEncryption.js @@ -266,7 +266,6 @@ export class RoomEncryption { async encrypt(type, content, hsApi) { const megolmResult = await this._megolmEncryption.encrypt(this._room.id, type, content, this._encryptionParams); if (megolmResult.roomKeyMessage) { - // TODO: should we await this?? this._shareNewRoomKey(megolmResult.roomKeyMessage, hsApi); } return { From 5f6ad91ff28df4d3545aa4a526bb2ac3db3024be Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 10 Nov 2020 11:04:09 +0100 Subject: [PATCH 08/13] offload creating an olm session to the olm worker so IE11 doesn't lock up when you start typing --- src/matrix/e2ee/Account.js | 8 ++++++-- src/matrix/e2ee/OlmWorker.js | 6 ++++++ src/matrix/e2ee/olm/Encryption.js | 2 +- src/platform/web/worker/main.js | 14 ++++++++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/matrix/e2ee/Account.js b/src/matrix/e2ee/Account.js index 7b4a5b0d..7d5075ae 100644 --- a/src/matrix/e2ee/Account.js +++ b/src/matrix/e2ee/Account.js @@ -149,10 +149,14 @@ export class Account { } } - createOutboundOlmSession(theirIdentityKey, theirOneTimeKey) { + async createOutboundOlmSession(theirIdentityKey, theirOneTimeKey) { const newSession = new this._olm.Session(); try { - newSession.create_outbound(this._account, theirIdentityKey, theirOneTimeKey); + if (this._olmWorker) { + await this._olmWorker.createOutboundOlmSession(this._account, newSession, theirIdentityKey, theirOneTimeKey); + } else { + newSession.create_outbound(this._account, theirIdentityKey, theirOneTimeKey); + } return newSession; } catch (err) { newSession.free(); diff --git a/src/matrix/e2ee/OlmWorker.js b/src/matrix/e2ee/OlmWorker.js index a6edd3cc..649db309 100644 --- a/src/matrix/e2ee/OlmWorker.js +++ b/src/matrix/e2ee/OlmWorker.js @@ -37,6 +37,12 @@ export class OlmWorker { account.unpickle("", pickle); } + async createOutboundSession(account, newSession, theirIdentityKey, theirOneTimeKey) { + const accountPickle = account.pickle(""); + const sessionPickle = await this._workerPool.send({type: "olm_create_outbound", accountPickle, theirIdentityKey, theirOneTimeKey}).response(); + newSession.unpickle("", sessionPickle); + } + dispose() { this._workerPool.dispose(); } diff --git a/src/matrix/e2ee/olm/Encryption.js b/src/matrix/e2ee/olm/Encryption.js index 18bbc5fa..8ce4583a 100644 --- a/src/matrix/e2ee/olm/Encryption.js +++ b/src/matrix/e2ee/olm/Encryption.js @@ -154,7 +154,7 @@ export class Encryption { try { for (const target of newEncryptionTargets) { const {device, oneTimeKey} = target; - target.session = this._account.createOutboundOlmSession(device.curve25519Key, oneTimeKey); + target.session = await this._account.createOutboundOlmSession(device.curve25519Key, oneTimeKey); } this._storeSessions(newEncryptionTargets, timestamp); } catch (err) { diff --git a/src/platform/web/worker/main.js b/src/platform/web/worker/main.js index a0016f67..e2413049 100644 --- a/src/platform/web/worker/main.js +++ b/src/platform/web/worker/main.js @@ -136,6 +136,18 @@ class MessageHandler { account.generate_one_time_keys(otkAmount); this._checkRandomValuesUsed(); return account.pickle(""); + _olmCreateOutbound(accountPickle, theirIdentityKey, theirOneTimeKey) { + return this._toMessage(() => { + const account = new this._olm.Account(); + const newSession = new this._olm.Session(); + try { + account.unpickle("", accountPickle); + newSession.create_outbound(account, newSession, theirIdentityKey, theirOneTimeKey); + return newSession.pickle(""); + } finally { + account.free(); + newSession.free(); + } }); } @@ -149,6 +161,8 @@ class MessageHandler { this._sendReply(message, this._megolmDecrypt(message.sessionKey, message.ciphertext)); } else if (type === "olm_create_account_otks") { this._sendReply(message, this._olmCreateAccountAndOTKs(message.randomValues, message.otkAmount)); + } else if (type === "olm_create_outbound") { + this._sendReply(message, this._olmCreateOutbound(message.accountPickle, message.theirIdentityKey, message.theirOneTimeKey)); } } } From f13f1cd593a1c15c926de2f95aff09bb7b7fdd3c Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 10 Nov 2020 11:04:39 +0100 Subject: [PATCH 09/13] prevent leaking resources in olm worker --- src/platform/web/worker/main.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/platform/web/worker/main.js b/src/platform/web/worker/main.js index e2413049..ae440f57 100644 --- a/src/platform/web/worker/main.js +++ b/src/platform/web/worker/main.js @@ -116,14 +116,13 @@ class MessageHandler { _megolmDecrypt(sessionKey, ciphertext) { return this._toMessage(() => { - let session; + const session = new this._olm.InboundGroupSession(); try { - session = new this._olm.InboundGroupSession(); session.import_session(sessionKey); // returns object with plaintext and message_index return session.decrypt(ciphertext); } finally { - session?.free(); + session.free(); } }); } @@ -132,10 +131,17 @@ class MessageHandler { return this._toMessage(() => { this._feedRandomValues(randomValues); const account = new this._olm.Account(); - account.create(); - account.generate_one_time_keys(otkAmount); - this._checkRandomValuesUsed(); - return account.pickle(""); + try { + account.create(); + account.generate_one_time_keys(otkAmount); + this._checkRandomValuesUsed(); + return account.pickle(""); + } finally { + account.free(); + } + }); + } + _olmCreateOutbound(accountPickle, theirIdentityKey, theirOneTimeKey) { return this._toMessage(() => { const account = new this._olm.Account(); From 89c66699d7f889cede4fb4a55314a83d8d050b74 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 10 Nov 2020 11:04:53 +0100 Subject: [PATCH 10/13] some Lock refactoring that I didn't end up needing but still useful --- src/utils/Lock.js | 22 ++++++++++++++-------- src/utils/LockMap.js | 6 ++---- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/utils/Lock.js b/src/utils/Lock.js index 6a198097..e133f33c 100644 --- a/src/utils/Lock.js +++ b/src/utils/Lock.js @@ -20,7 +20,7 @@ export class Lock { this._resolve = null; } - take() { + tryTake() { if (!this._promise) { this._promise = new Promise(resolve => { this._resolve = resolve; @@ -30,6 +30,12 @@ export class Lock { return false; } + async take() { + while(!this.tryTake()) { + await this.released(); + } + } + get isTaken() { return !!this._promise; } @@ -52,25 +58,25 @@ export function tests() { return { "taking a lock twice returns false": assert => { const lock = new Lock(); - assert.equal(lock.take(), true); + assert.equal(lock.tryTake(), true); assert.equal(lock.isTaken, true); - assert.equal(lock.take(), false); + assert.equal(lock.tryTake(), false); }, "can take a released lock again": assert => { const lock = new Lock(); - lock.take(); + lock.tryTake(); lock.release(); assert.equal(lock.isTaken, false); - assert.equal(lock.take(), true); + assert.equal(lock.tryTake(), true); }, "2 waiting for lock, only first one gets it": async assert => { const lock = new Lock(); - lock.take(); + lock.tryTake(); let first; - lock.released().then(() => first = lock.take()); + lock.released().then(() => first = lock.tryTake()); let second; - lock.released().then(() => second = lock.take()); + lock.released().then(() => second = lock.tryTake()); const promise = lock.released(); lock.release(); await promise; diff --git a/src/utils/LockMap.js b/src/utils/LockMap.js index f99776cc..a73dee4a 100644 --- a/src/utils/LockMap.js +++ b/src/utils/LockMap.js @@ -24,12 +24,10 @@ export class LockMap { async takeLock(key) { let lock = this._map.get(key); if (lock) { - while (!lock.take()) { - await lock.released(); - } + await lock.take(); } else { lock = new Lock(); - lock.take(); + lock.tryTake(); this._map.set(key, lock); } // don't leave old locks lying around From fd498b3d24df28b60ab8a8e204a07339b67edb62 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 10 Nov 2020 14:02:07 +0100 Subject: [PATCH 11/13] no need to keep track of promise, fn is internally rate-limited now --- src/domain/session/room/RoomViewModel.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/domain/session/room/RoomViewModel.js b/src/domain/session/room/RoomViewModel.js index c872ca0f..8811625a 100644 --- a/src/domain/session/room/RoomViewModel.js +++ b/src/domain/session/room/RoomViewModel.js @@ -169,7 +169,6 @@ class ComposerViewModel extends ViewModel { super(); this._roomVM = roomVM; this._isEmpty = true; - this._ensureKeyPromise = null; } get isEncrypted() { @@ -192,10 +191,8 @@ class ComposerViewModel extends ViewModel { async setInput(text) { const wasEmpty = this._isEmpty; this._isEmpty = text.length === 0; - if (wasEmpty && !this._isEmpty && !this._ensureKeyPromise) { - this._ensureKeyPromise = this._roomVM._room.ensureMessageKeyIsShared().then(() => { - this._ensureKeyPromise = null; - }); + if (wasEmpty && !this._isEmpty) { + this._roomVM._room.ensureMessageKeyIsShared(); } if (wasEmpty !== this._isEmpty) { this.emitChange("canSend"); From 769feac73c060313c3446194ecce694c20b62ba3 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 10 Nov 2020 14:02:27 +0100 Subject: [PATCH 12/13] cleanup --- src/matrix/e2ee/RoomEncryption.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/matrix/e2ee/RoomEncryption.js b/src/matrix/e2ee/RoomEncryption.js index 86c07606..54042bbd 100644 --- a/src/matrix/e2ee/RoomEncryption.js +++ b/src/matrix/e2ee/RoomEncryption.js @@ -253,7 +253,7 @@ export class RoomEncryption { /** shares the encryption key for the next message if needed */ async ensureMessageKeyIsShared(hsApi) { - if (this._lastKeyPreShareTime && this._lastKeyPreShareTime.measure() < MIN_PRESHARE_INTERVAL) { + if (this._lastKeyPreShareTime?.measure() < MIN_PRESHARE_INTERVAL) { return; } this._lastKeyPreShareTime = this._clock.createMeasure(); From e4fa4ded6c3cd5d360f99d944996d00679f4f57d Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 10 Nov 2020 14:02:37 +0100 Subject: [PATCH 13/13] hide clear button in composer on IE --- src/platform/web/ui/css/main.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/platform/web/ui/css/main.css b/src/platform/web/ui/css/main.css index 496f76db..aa22839e 100644 --- a/src/platform/web/ui/css/main.css +++ b/src/platform/web/ui/css/main.css @@ -44,3 +44,8 @@ body.hydrogen { .hidden { display: none !important; } + +/* hide clear buttons in IE */ +input::-ms-clear { + display: none; +}