Skip to content

Commit f92b620

Browse files
committed
Merge branch 'develop' into room-history-key-sharing2
2 parents 6381018 + bf25cb6 commit f92b620

24 files changed

+631
-235
lines changed

CHANGELOG.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,58 @@
1+
Changes in [9.9.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.9.0) (2021-03-15)
2+
================================================================================================
3+
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.9.0-rc.1...v9.9.0)
4+
5+
* No changes since rc.1
6+
7+
Changes in [9.9.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.9.0-rc.1) (2021-03-10)
8+
==========================================================================================================
9+
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.8.0...v9.9.0-rc.1)
10+
11+
* Remove detailed Olm session logging
12+
[\#1638](https://github.com/matrix-org/matrix-js-sdk/pull/1638)
13+
* Add space summary suggested only param
14+
[\#1637](https://github.com/matrix-org/matrix-js-sdk/pull/1637)
15+
* Check TURN servers periodically, and at start of calls
16+
[\#1634](https://github.com/matrix-org/matrix-js-sdk/pull/1634)
17+
* Support sending invite reasons
18+
[\#1624](https://github.com/matrix-org/matrix-js-sdk/pull/1624)
19+
* Bump elliptic from 6.5.3 to 6.5.4
20+
[\#1636](https://github.com/matrix-org/matrix-js-sdk/pull/1636)
21+
* Add a function to get a room's MXC URI
22+
[\#1635](https://github.com/matrix-org/matrix-js-sdk/pull/1635)
23+
* Stop streams if the call has ended
24+
[\#1633](https://github.com/matrix-org/matrix-js-sdk/pull/1633)
25+
* Remove export keyword from global.d.ts
26+
[\#1631](https://github.com/matrix-org/matrix-js-sdk/pull/1631)
27+
* Fix IndexedDB store creation example
28+
[\#1445](https://github.com/matrix-org/matrix-js-sdk/pull/1445)
29+
* An attempt to cleanup how constraints are handled in calls
30+
[\#1613](https://github.com/matrix-org/matrix-js-sdk/pull/1613)
31+
* Extract display name patterns to constants
32+
[\#1628](https://github.com/matrix-org/matrix-js-sdk/pull/1628)
33+
* Bump pug-code-gen from 2.0.2 to 2.0.3
34+
[\#1630](https://github.com/matrix-org/matrix-js-sdk/pull/1630)
35+
* Avoid deadlocks when ensuring Olm sessions for devices
36+
[\#1627](https://github.com/matrix-org/matrix-js-sdk/pull/1627)
37+
* Filter out edits from other senders in history
38+
[\#1626](https://github.com/matrix-org/matrix-js-sdk/pull/1626)
39+
* Fix ContentHelpers export
40+
[\#1618](https://github.com/matrix-org/matrix-js-sdk/pull/1618)
41+
* Add logging to in progress Olm sessions
42+
[\#1621](https://github.com/matrix-org/matrix-js-sdk/pull/1621)
43+
* Don't ignore ICE candidates received before offer/answer
44+
[\#1623](https://github.com/matrix-org/matrix-js-sdk/pull/1623)
45+
* Better handling of send failures on VoIP events
46+
[\#1622](https://github.com/matrix-org/matrix-js-sdk/pull/1622)
47+
* Log when turn creds expire
48+
[\#1620](https://github.com/matrix-org/matrix-js-sdk/pull/1620)
49+
* Initial Spaces [MSC1772] support
50+
[\#1563](https://github.com/matrix-org/matrix-js-sdk/pull/1563)
51+
* Add logging to crypto store transactions
52+
[\#1617](https://github.com/matrix-org/matrix-js-sdk/pull/1617)
53+
* Room helper for getting type and checking if it is a space room
54+
[\#1610](https://github.com/matrix-org/matrix-js-sdk/pull/1610)
55+
156
Changes in [9.8.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.8.0) (2021-03-01)
257
================================================================================================
358
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.8.0-rc.1...v9.8.0)

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "matrix-js-sdk",
3-
"version": "9.8.0",
3+
"version": "9.9.0",
44
"description": "Matrix Client-Server SDK for Javascript",
55
"scripts": {
66
"prepublishOnly": "yarn build",
@@ -15,7 +15,7 @@
1515
"build:minify-browser": "terser dist/browser-matrix.js --compress --mangle --source-map --output dist/browser-matrix.min.js",
1616
"gendoc": "jsdoc -c jsdoc.json -P package.json",
1717
"lint": "yarn lint:types && yarn lint:js",
18-
"lint:js": "eslint --max-warnings 73 src spec",
18+
"lint:js": "eslint --max-warnings 72 src spec",
1919
"lint:types": "tsc --noEmit",
2020
"test": "jest spec/ --coverage --testEnvironment node",
2121
"test:watch": "jest spec/ --coverage --testEnvironment node --watch"

spec/unit/crypto/DeviceList.spec.js

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,36 @@ const signedDeviceList = {
5151
},
5252
};
5353

54+
const signedDeviceList2 = {
55+
"failures": {},
56+
"device_keys": {
57+
"@test2:sw1v.org": {
58+
"QJVRHWAKGH": {
59+
"signatures": {
60+
"@test2:sw1v.org": {
61+
"ed25519:QJVRHWAKGH":
62+
"w1xxdLe1iIqzEFHLRVYQeuiM6t2N2ZRiI8s5nDKxf054BP8" +
63+
"1CPEX/AQXh5BhkKAVMlKnwg4T9zU1/wBALeajk3",
64+
},
65+
},
66+
"user_id": "@test2:sw1v.org",
67+
"keys": {
68+
"ed25519:QJVRHWAKGH":
69+
"Ig0/C6T+bBII1l2By2Wnnvtjp1nm/iXBlLU5/QESFXL",
70+
"curve25519:QJVRHWAKGH":
71+
"YR3eQnUvTQzGlWih4rsmJkKxpDxzgkgIgsBd1DEZIbm",
72+
},
73+
"algorithms": [
74+
"m.olm.v1.curve25519-aes-sha2",
75+
"m.megolm.v1.aes-sha2",
76+
],
77+
"device_id": "QJVRHWAKGH",
78+
"unsigned": {},
79+
},
80+
},
81+
},
82+
};
83+
5484
describe('DeviceList', function() {
5585
let downloadSpy;
5686
let cryptoStore;
@@ -69,7 +99,7 @@ describe('DeviceList', function() {
6999
}
70100
});
71101

72-
function createTestDeviceList() {
102+
function createTestDeviceList(keyDownloadChunkSize = 250) {
73103
const baseApis = {
74104
downloadKeysForUsers: downloadSpy,
75105
getUserId: () => '@test1:sw1v.org',
@@ -78,7 +108,7 @@ describe('DeviceList', function() {
78108
const mockOlm = {
79109
verifySignature: function(key, message, signature) {},
80110
};
81-
const dl = new DeviceList(baseApis, cryptoStore, mockOlm);
111+
const dl = new DeviceList(baseApis, cryptoStore, mockOlm, keyDownloadChunkSize);
82112
deviceLists.push(dl);
83113
return dl;
84114
}
@@ -150,4 +180,30 @@ describe('DeviceList', function() {
150180
expect(Object.keys(storedKeys)).toEqual(['HGKAWHRVJQ']);
151181
});
152182
});
183+
184+
it("should download device keys in batches", function() {
185+
const dl = createTestDeviceList(1);
186+
187+
dl.startTrackingDeviceList('@test1:sw1v.org');
188+
dl.startTrackingDeviceList('@test2:sw1v.org');
189+
190+
const queryDefer1 = utils.defer();
191+
downloadSpy.mockReturnValueOnce(queryDefer1.promise);
192+
const queryDefer2 = utils.defer();
193+
downloadSpy.mockReturnValueOnce(queryDefer2.promise);
194+
195+
const prom1 = dl.refreshOutdatedDeviceLists();
196+
expect(downloadSpy).toBeCalledTimes(2);
197+
expect(downloadSpy).toHaveBeenNthCalledWith(1, ['@test1:sw1v.org'], {});
198+
expect(downloadSpy).toHaveBeenNthCalledWith(2, ['@test2:sw1v.org'], {});
199+
queryDefer1.resolve(utils.deepCopy(signedDeviceList));
200+
queryDefer2.resolve(utils.deepCopy(signedDeviceList2));
201+
202+
return prom1.then(() => {
203+
const storedKeys1 = dl.getRawStoredDevicesForUser('@test1:sw1v.org');
204+
expect(Object.keys(storedKeys1)).toEqual(['HGKAWHRVJQ']);
205+
const storedKeys2 = dl.getRawStoredDevicesForUser('@test2:sw1v.org');
206+
expect(Object.keys(storedKeys2)).toEqual(['QJVRHWAKGH']);
207+
});
208+
});
153209
});

spec/unit/crypto/algorithms/olm.spec.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,5 +190,91 @@ describe("OlmDevice", function() {
190190
// new session and will have called claimOneTimeKeys
191191
expect(count).toBe(2);
192192
});
193+
194+
it("avoids deadlocks when two tasks are ensuring the same devices", async function() {
195+
// This test checks whether `ensureOlmSessionsForDevices` properly
196+
// handles multiple tasks in flight ensuring some set of devices in
197+
// common without deadlocks.
198+
199+
let claimRequestCount = 0;
200+
const baseApis = {
201+
claimOneTimeKeys: () => {
202+
// simulate a very slow server (.5 seconds to respond)
203+
claimRequestCount++;
204+
return new Promise((resolve, reject) => {
205+
setTimeout(reject, 500);
206+
});
207+
},
208+
};
209+
210+
const deviceBobA = DeviceInfo.fromStorage({
211+
keys: {
212+
"curve25519:BOB-A": "akey",
213+
},
214+
}, "BOB-A");
215+
const deviceBobB = DeviceInfo.fromStorage({
216+
keys: {
217+
"curve25519:BOB-B": "bkey",
218+
},
219+
}, "BOB-B");
220+
221+
// There's no required ordering of devices per user, so here we
222+
// create two different orderings so that each task reserves a
223+
// device the other task needs before continuing.
224+
const devicesByUserAB = {
225+
"@bob:example.com": [
226+
deviceBobA,
227+
deviceBobB,
228+
],
229+
};
230+
const devicesByUserBA = {
231+
"@bob:example.com": [
232+
deviceBobB,
233+
deviceBobA,
234+
],
235+
};
236+
237+
function alwaysSucceed(promise) {
238+
// swallow any exception thrown by a promise, so that
239+
// Promise.all doesn't abort
240+
return promise.catch(() => {});
241+
}
242+
243+
const task1 = alwaysSucceed(olmlib.ensureOlmSessionsForDevices(
244+
aliceOlmDevice, baseApis, devicesByUserAB,
245+
));
246+
247+
// After a single tick through the first task, it should have
248+
// claimed ownership of all devices to avoid deadlocking others.
249+
expect(Object.keys(aliceOlmDevice._sessionsInProgress).length).toBe(2);
250+
251+
const task2 = alwaysSucceed(olmlib.ensureOlmSessionsForDevices(
252+
aliceOlmDevice, baseApis, devicesByUserBA,
253+
));
254+
255+
// The second task should not have changed the ownership count, as
256+
// it's waiting on the first task.
257+
expect(Object.keys(aliceOlmDevice._sessionsInProgress).length).toBe(2);
258+
259+
// Track the tasks, but don't await them yet.
260+
const promises = Promise.all([
261+
task1,
262+
task2,
263+
]);
264+
265+
await new Promise((resolve) => {
266+
setTimeout(resolve, 200);
267+
});
268+
269+
// After .2s, the first task should have made an initial claim request.
270+
expect(claimRequestCount).toBe(1);
271+
272+
await promises;
273+
274+
// After waiting for both tasks to complete, the first task should
275+
// have failed, so the second task should have tried to create a
276+
// new session and will have called claimOneTimeKeys
277+
expect(claimRequestCount).toBe(2);
278+
});
193279
});
194280
});

spec/unit/crypto/cross-signing.spec.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,9 @@ describe("Cross Signing", function() {
193193
const keyChangePromise = new Promise((resolve, reject) => {
194194
alice.once("crossSigning.keysChanged", async (e) => {
195195
resolve(e);
196-
await alice.checkOwnCrossSigningTrust();
196+
await alice.checkOwnCrossSigningTrust({
197+
allowPrivateKeyRequests: true,
198+
});
197199
});
198200
});
199201

spec/unit/utils.spec.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,4 +282,30 @@ describe("utils", function() {
282282
expect(target.nonenumerableProp).toBe(undefined);
283283
});
284284
});
285+
286+
describe("chunkPromises", function() {
287+
it("should execute promises in chunks", async function() {
288+
let promiseCount = 0;
289+
290+
function fn1() {
291+
return new Promise(async function(resolve, reject) {
292+
await utils.sleep(1);
293+
expect(promiseCount).toEqual(0);
294+
++promiseCount;
295+
resolve();
296+
});
297+
}
298+
299+
function fn2() {
300+
return new Promise(function(resolve, reject) {
301+
expect(promiseCount).toEqual(1);
302+
++promiseCount;
303+
resolve();
304+
});
305+
}
306+
307+
await utils.chunkPromises([fn1, fn2], 1);
308+
expect(promiseCount).toEqual(2);
309+
});
310+
});
285311
});

spec/unit/webrtc/call.spec.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class MockRTCPeerConnection {
7979
return Promise.resolve();
8080
}
8181
close() {}
82+
getStats() { return []; }
8283
}
8384

8485
describe('Call', function() {
@@ -122,6 +123,7 @@ describe('Call', function() {
122123
// We just stub out sendEvent: we're not interested in testing the client's
123124
// event sending code here
124125
client.client.sendEvent = () => {};
126+
client.httpBackend.when("GET", "/voip/turnServer").respond(200, {});
125127
call = new MatrixCall({
126128
client: client.client,
127129
roomId: '!foo:bar',
@@ -138,7 +140,9 @@ describe('Call', function() {
138140
});
139141

140142
it('should ignore candidate events from non-matching party ID', async function() {
141-
await call.placeVoiceCall();
143+
const callPromise = call.placeVoiceCall();
144+
await client.httpBackend.flush();
145+
await callPromise;
142146
await call.onAnswerReceived({
143147
getContent: () => {
144148
return {
@@ -192,7 +196,9 @@ describe('Call', function() {
192196
});
193197

194198
it('should add candidates received before answer if party ID is correct', async function() {
195-
await call.placeVoiceCall();
199+
const callPromise = call.placeVoiceCall();
200+
await client.httpBackend.flush();
201+
await callPromise;
196202
call.peerConn.addIceCandidate = jest.fn();
197203

198204
call.onRemoteIceCandidatesReceived({

src/@types/global.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ declare global {
4141
getUserMedia(constraints: MediaStreamConstraints | DesktopCapturerConstraints): Promise<MediaStream>;
4242
}
4343

44-
export interface DesktopCapturerConstraints {
44+
interface DesktopCapturerConstraints {
4545
audio: boolean | {
4646
mandatory: {
4747
chromeMediaSource: string;
@@ -56,7 +56,7 @@ declare global {
5656
};
5757
}
5858

59-
export interface DesktopCapturerSource {
59+
interface DesktopCapturerSource {
6060
id: string;
6161
name: string;
6262
thumbnailURL: string;

0 commit comments

Comments
 (0)