@mertushka/webrtc-node 0.1.0-alpha.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +373 -373
- package/README.md +72 -79
- package/docs/conformance.md +10 -0
- package/docs/development.md +14 -2
- package/lib/index.js +339 -76
- package/package.json +1 -1
- package/scripts/check-native-integration.js +17 -2
- package/scripts/install-native.js +5 -0
- package/scripts/run-docker-linux-ci.ps1 +73 -73
- package/src/native/addon.cc +121 -40
package/lib/index.js
CHANGED
|
@@ -53,17 +53,23 @@ class SimpleEventTarget {
|
|
|
53
53
|
map.set(key, listeners);
|
|
54
54
|
}
|
|
55
55
|
const once = typeof options === "object" && options !== null && Boolean(options.once);
|
|
56
|
+
let added = false;
|
|
56
57
|
if (!listeners.some((listener) => listener.callback === callback)) {
|
|
57
58
|
listeners.push({ callback, once });
|
|
59
|
+
added = true;
|
|
58
60
|
}
|
|
59
|
-
if (typeof this._eventListenerAdded === "function") this._eventListenerAdded(key);
|
|
61
|
+
if (added && typeof this._eventListenerAdded === "function") this._eventListenerAdded(key);
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
removeEventListener(type, callback) {
|
|
63
|
-
const
|
|
65
|
+
const key = String(type);
|
|
66
|
+
const listeners = kHandlers.get(this)?.get(key);
|
|
64
67
|
if (!listeners) return;
|
|
65
68
|
const index = listeners.findIndex((listener) => listener.callback === callback);
|
|
66
|
-
if (index !== -1)
|
|
69
|
+
if (index !== -1) {
|
|
70
|
+
listeners.splice(index, 1);
|
|
71
|
+
if (typeof this._eventListenerRemoved === "function") this._eventListenerRemoved(key);
|
|
72
|
+
}
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
dispatchEvent(event) {
|
|
@@ -72,11 +78,14 @@ class SimpleEventTarget {
|
|
|
72
78
|
}
|
|
73
79
|
event.target = event.target || this;
|
|
74
80
|
event.currentTarget = this;
|
|
75
|
-
const
|
|
81
|
+
const registeredListeners = kHandlers.get(this)?.get(event.type);
|
|
82
|
+
const listeners = registeredListeners?.length ? Array.from(registeredListeners) : null;
|
|
76
83
|
const handler = this[`on${event.type}`];
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
if (listeners) {
|
|
85
|
+
for (const listener of listeners) {
|
|
86
|
+
callListener(listener.callback, this, event);
|
|
87
|
+
if (listener.once) this.removeEventListener(event.type, listener.callback);
|
|
88
|
+
}
|
|
80
89
|
}
|
|
81
90
|
if (typeof handler === "function") callListener(handler, this, event);
|
|
82
91
|
return !event.defaultPrevented;
|
|
@@ -477,7 +486,7 @@ function maxMessageSizeFromSdp(description) {
|
|
|
477
486
|
}
|
|
478
487
|
|
|
479
488
|
function nextTask() {
|
|
480
|
-
return new Promise((resolve) =>
|
|
489
|
+
return new Promise((resolve) => setImmediate(resolve));
|
|
481
490
|
}
|
|
482
491
|
|
|
483
492
|
function delay(ms) {
|
|
@@ -1283,6 +1292,9 @@ class RTCDataChannel extends SimpleEventTarget {
|
|
|
1283
1292
|
);
|
|
1284
1293
|
peerConnection._channels.set(channel._native.bindingId, channel);
|
|
1285
1294
|
peerConnection._registerDataChannelId(channel);
|
|
1295
|
+
if (channel._registeredDataChannelId == null && channel._effectiveId() == null) {
|
|
1296
|
+
peerConnection._dataChannelIdRefreshNeeded = true;
|
|
1297
|
+
}
|
|
1286
1298
|
return channel;
|
|
1287
1299
|
}
|
|
1288
1300
|
|
|
@@ -1305,9 +1317,12 @@ class RTCDataChannel extends SimpleEventTarget {
|
|
|
1305
1317
|
this._nativeEventDrainActive = false;
|
|
1306
1318
|
this._queuedNativeEvents = [];
|
|
1307
1319
|
this._queuedMessageEvents = [];
|
|
1320
|
+
this._queuedMessageEventIndex = 0;
|
|
1308
1321
|
this._messageEventFlushScheduled = false;
|
|
1309
1322
|
this._messageEventGateActive = false;
|
|
1310
1323
|
this._messageConsumerGateActive = false;
|
|
1324
|
+
this._messageEventListenerCount = 0;
|
|
1325
|
+
this._messageHandlerNeedsTaskYield = false;
|
|
1311
1326
|
this._pendingPairedDeliveryBytes = 0;
|
|
1312
1327
|
this._nativeCloseEventQueued = false;
|
|
1313
1328
|
this._openEventDeferredForIce = false;
|
|
@@ -1391,6 +1406,7 @@ class RTCDataChannel extends SimpleEventTarget {
|
|
|
1391
1406
|
|
|
1392
1407
|
set onmessage(callback) {
|
|
1393
1408
|
this._onmessage = typeof callback === "function" ? callback : null;
|
|
1409
|
+
this._messageHandlerNeedsTaskYield = this._messageContinuationNeedsTaskYield(this._onmessage);
|
|
1394
1410
|
if (this._onmessage) this._releaseMessageConsumerGate();
|
|
1395
1411
|
}
|
|
1396
1412
|
|
|
@@ -1428,17 +1444,17 @@ class RTCDataChannel extends SimpleEventTarget {
|
|
|
1428
1444
|
return;
|
|
1429
1445
|
}
|
|
1430
1446
|
|
|
1431
|
-
const view = toUint8Array(data);
|
|
1447
|
+
const view = data instanceof Uint8Array ? data : toUint8Array(data);
|
|
1432
1448
|
const size = view.byteLength;
|
|
1433
1449
|
this._assertWithinMaxMessageSize(size);
|
|
1434
1450
|
if (this._hasPendingSends() || !this._nativeReadyForSend()) {
|
|
1435
1451
|
const payload = new Uint8Array(view);
|
|
1436
1452
|
this._enqueueSend(() => this._sendNativeBinary(payload), size);
|
|
1437
1453
|
} else {
|
|
1438
|
-
|
|
1439
|
-
if (this._sendNativeBinary(payload)) {
|
|
1454
|
+
if (this._sendNativeBinary(view)) {
|
|
1440
1455
|
this._increaseBufferedAmount(size);
|
|
1441
1456
|
} else {
|
|
1457
|
+
const payload = new Uint8Array(view);
|
|
1442
1458
|
this._enqueueSend(() => this._sendNativeBinary(payload), size);
|
|
1443
1459
|
}
|
|
1444
1460
|
}
|
|
@@ -1457,11 +1473,7 @@ class RTCDataChannel extends SimpleEventTarget {
|
|
|
1457
1473
|
this._nativeCloseScheduled = true;
|
|
1458
1474
|
const closeNative = () => {
|
|
1459
1475
|
const close = () => {
|
|
1460
|
-
const synthesizePairedClose =
|
|
1461
|
-
pairedChannel &&
|
|
1462
|
-
pairedChannel.readyState !== "closed" &&
|
|
1463
|
-
(pairedChannel._syntheticIncoming ||
|
|
1464
|
-
(this._bufferedAmount === 0 && !this._hasPendingSends()));
|
|
1476
|
+
const synthesizePairedClose = pairedChannel && pairedChannel.readyState !== "closed";
|
|
1465
1477
|
this._native.close();
|
|
1466
1478
|
if (synthesizePairedClose) {
|
|
1467
1479
|
setTimeout(() => pairedChannel._handleRemoteChannelClose(), 0);
|
|
@@ -1473,7 +1485,7 @@ class RTCDataChannel extends SimpleEventTarget {
|
|
|
1473
1485
|
if (shouldDrainBeforeClose) {
|
|
1474
1486
|
this._closeNativeAfterBufferedSends(close);
|
|
1475
1487
|
} else {
|
|
1476
|
-
close
|
|
1488
|
+
setImmediate(close);
|
|
1477
1489
|
}
|
|
1478
1490
|
};
|
|
1479
1491
|
if (this._hasPendingSends()) {
|
|
@@ -1487,7 +1499,7 @@ class RTCDataChannel extends SimpleEventTarget {
|
|
|
1487
1499
|
_increaseBufferedAmount(size) {
|
|
1488
1500
|
if (!size) return;
|
|
1489
1501
|
this._bufferedAmount += size;
|
|
1490
|
-
|
|
1502
|
+
setImmediate(() => this._decreaseBufferedAmount(size));
|
|
1491
1503
|
}
|
|
1492
1504
|
|
|
1493
1505
|
_hasPendingSends() {
|
|
@@ -1527,8 +1539,12 @@ class RTCDataChannel extends SimpleEventTarget {
|
|
|
1527
1539
|
}
|
|
1528
1540
|
|
|
1529
1541
|
_assertWithinMaxMessageSize(size) {
|
|
1530
|
-
const maxMessageSize = this._pc.
|
|
1531
|
-
if (
|
|
1542
|
+
const maxMessageSize = this._pc._sctpTransport?._maxMessageSize;
|
|
1543
|
+
if (
|
|
1544
|
+
maxMessageSize !== null &&
|
|
1545
|
+
maxMessageSize !== Number.POSITIVE_INFINITY &&
|
|
1546
|
+
size > maxMessageSize
|
|
1547
|
+
) {
|
|
1532
1548
|
throw new TypeError("RTCDataChannel message exceeds RTCSctpTransport.maxMessageSize");
|
|
1533
1549
|
}
|
|
1534
1550
|
}
|
|
@@ -1589,8 +1605,13 @@ class RTCDataChannel extends SimpleEventTarget {
|
|
|
1589
1605
|
return true;
|
|
1590
1606
|
}
|
|
1591
1607
|
try {
|
|
1592
|
-
|
|
1593
|
-
if (sent)
|
|
1608
|
+
let sent = this._native.sendString(data);
|
|
1609
|
+
if (sent !== false) {
|
|
1610
|
+
sent = true;
|
|
1611
|
+
} else {
|
|
1612
|
+
sent = Boolean(this._native?.isOpen);
|
|
1613
|
+
}
|
|
1614
|
+
if (sent && this._pairedChannel) this._pendingPairedDeliveryBytes += byteLength(data);
|
|
1594
1615
|
return sent;
|
|
1595
1616
|
} catch (error) {
|
|
1596
1617
|
throw mapNativeError(error, "OperationError");
|
|
@@ -1606,21 +1627,22 @@ class RTCDataChannel extends SimpleEventTarget {
|
|
|
1606
1627
|
return true;
|
|
1607
1628
|
}
|
|
1608
1629
|
try {
|
|
1609
|
-
const view = toUint8Array(data);
|
|
1610
|
-
|
|
1611
|
-
if (sent)
|
|
1630
|
+
const view = data instanceof Uint8Array ? data : toUint8Array(data);
|
|
1631
|
+
let sent = this._native.sendBinary(view);
|
|
1632
|
+
if (sent !== false) {
|
|
1633
|
+
sent = true;
|
|
1634
|
+
} else {
|
|
1635
|
+
sent = Boolean(this._native?.isOpen);
|
|
1636
|
+
}
|
|
1637
|
+
if (sent && this._pairedChannel && view.byteLength > 0) {
|
|
1638
|
+
this._pendingPairedDeliveryBytes += view.byteLength;
|
|
1639
|
+
}
|
|
1612
1640
|
return sent;
|
|
1613
1641
|
} catch (error) {
|
|
1614
1642
|
throw mapNativeError(error, "OperationError");
|
|
1615
1643
|
}
|
|
1616
1644
|
}
|
|
1617
1645
|
|
|
1618
|
-
_tryNativeSend(send) {
|
|
1619
|
-
const sent = send();
|
|
1620
|
-
if (sent !== false) return true;
|
|
1621
|
-
return Boolean(this._native?.isOpen);
|
|
1622
|
-
}
|
|
1623
|
-
|
|
1624
1646
|
_usesSyntheticPairDelivery() {
|
|
1625
1647
|
return this._syntheticIncoming || this._pairedChannel?._syntheticIncoming;
|
|
1626
1648
|
}
|
|
@@ -1628,10 +1650,10 @@ class RTCDataChannel extends SimpleEventTarget {
|
|
|
1628
1650
|
_deliverSyntheticMessage(event) {
|
|
1629
1651
|
const target = this._pairedChannel;
|
|
1630
1652
|
if (!target || target.readyState === "closed") return;
|
|
1631
|
-
|
|
1653
|
+
setImmediate(() => {
|
|
1632
1654
|
if (target.readyState === "closed") return;
|
|
1633
1655
|
target._handleNativeEvent({ type: "message", ...event });
|
|
1634
|
-
}
|
|
1656
|
+
});
|
|
1635
1657
|
}
|
|
1636
1658
|
|
|
1637
1659
|
_trackPendingPairedDelivery(size) {
|
|
@@ -1645,6 +1667,7 @@ class RTCDataChannel extends SimpleEventTarget {
|
|
|
1645
1667
|
const size = event.binary ? event.data?.byteLength || 0 : byteLength(event.data || "");
|
|
1646
1668
|
if (size <= 0) return;
|
|
1647
1669
|
sender._pendingPairedDeliveryBytes = Math.max(0, sender._pendingPairedDeliveryBytes - size);
|
|
1670
|
+
sender._decreaseBufferedAmount(size);
|
|
1648
1671
|
}
|
|
1649
1672
|
|
|
1650
1673
|
_decreaseBufferedAmount(size) {
|
|
@@ -1839,6 +1862,9 @@ class RTCDataChannel extends SimpleEventTarget {
|
|
|
1839
1862
|
if (this._pairedChannel?._pairedChannel === this) this._pairedChannel._pairedChannel = null;
|
|
1840
1863
|
this._pairedChannel = null;
|
|
1841
1864
|
this._pc._unregisterDataChannelId(this);
|
|
1865
|
+
if (this._pc._channels.get(this._native.bindingId) === this) {
|
|
1866
|
+
this._pc._channels.delete(this._native.bindingId);
|
|
1867
|
+
}
|
|
1842
1868
|
if (!this._createdLocally && id != null) {
|
|
1843
1869
|
this._pc._remoteAnnouncedDataChannelIds.delete(id);
|
|
1844
1870
|
this._pc._pendingSyntheticDataChannelAnnouncements.delete(id);
|
|
@@ -1921,10 +1947,10 @@ class RTCDataChannel extends SimpleEventTarget {
|
|
|
1921
1947
|
return;
|
|
1922
1948
|
}
|
|
1923
1949
|
this._handleNativeEvent(event, true);
|
|
1924
|
-
if (this._queuedNativeEvents.length)
|
|
1950
|
+
if (this._queuedNativeEvents.length) setImmediate(dispatchNext);
|
|
1925
1951
|
else this._nativeEventDrainActive = false;
|
|
1926
1952
|
};
|
|
1927
|
-
|
|
1953
|
+
setImmediate(dispatchNext);
|
|
1928
1954
|
}
|
|
1929
1955
|
|
|
1930
1956
|
_queueMessageEvent(event) {
|
|
@@ -1932,39 +1958,175 @@ class RTCDataChannel extends SimpleEventTarget {
|
|
|
1932
1958
|
this._scheduleMessageEventFlush();
|
|
1933
1959
|
}
|
|
1934
1960
|
|
|
1961
|
+
_queueMessageEvents(events) {
|
|
1962
|
+
for (const event of events) this._queuedMessageEvents.push(event);
|
|
1963
|
+
this._scheduleMessageEventFlush();
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1935
1966
|
_scheduleMessageEventFlush() {
|
|
1936
1967
|
if (this._messageEventFlushScheduled) return;
|
|
1937
1968
|
this._messageEventFlushScheduled = true;
|
|
1938
|
-
|
|
1969
|
+
setImmediate(() => {
|
|
1939
1970
|
this._messageEventFlushScheduled = false;
|
|
1940
1971
|
this._flushQueuedMessageEvents();
|
|
1941
|
-
}
|
|
1972
|
+
});
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
_scheduleMessageEventContinuation({ yieldToTask = false } = {}) {
|
|
1976
|
+
if (this._messageEventFlushScheduled) return;
|
|
1977
|
+
this._messageEventFlushScheduled = true;
|
|
1978
|
+
const schedule = yieldToTask ? setTimeout : setImmediate;
|
|
1979
|
+
schedule(
|
|
1980
|
+
() => {
|
|
1981
|
+
this._messageEventFlushScheduled = false;
|
|
1982
|
+
this._flushQueuedMessageEvents();
|
|
1983
|
+
},
|
|
1984
|
+
yieldToTask ? 1 : 0,
|
|
1985
|
+
);
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
_messageContinuationNeedsTaskYield(handler = this._onmessage) {
|
|
1989
|
+
if (typeof handler !== "function") return false;
|
|
1990
|
+
try {
|
|
1991
|
+
return /\[native code\]/.test(Function.prototype.toString.call(handler));
|
|
1992
|
+
} catch {
|
|
1993
|
+
return false;
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
_hasMessageEventListeners() {
|
|
1998
|
+
return this._messageEventListenerCount > 0;
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
_canContinueMessageBatchInCurrentTask(handler, yieldToTask) {
|
|
2002
|
+
return (
|
|
2003
|
+
typeof handler === "function" &&
|
|
2004
|
+
handler instanceof Function &&
|
|
2005
|
+
this._onmessage === handler &&
|
|
2006
|
+
!yieldToTask &&
|
|
2007
|
+
!this._hasMessageEventListeners()
|
|
2008
|
+
);
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
_canUseDirectMessageHandler(handler, yieldToTask, hasMessageListeners) {
|
|
2012
|
+
return !hasMessageListeners && this._canContinueMessageBatchInCurrentTask(handler, yieldToTask);
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
_dispatchMessageEvent(event, directHandler) {
|
|
2016
|
+
const messageEvent = makeMessageEvent("message", { data: this._convertMessage(event) });
|
|
2017
|
+
if (directHandler) {
|
|
2018
|
+
messageEvent.target = this;
|
|
2019
|
+
messageEvent.currentTarget = this;
|
|
2020
|
+
callListener(directHandler, this, messageEvent);
|
|
2021
|
+
} else {
|
|
2022
|
+
this.dispatchEvent(messageEvent);
|
|
2023
|
+
}
|
|
1942
2024
|
}
|
|
1943
2025
|
|
|
1944
2026
|
_flushQueuedMessageEvents() {
|
|
1945
|
-
|
|
1946
|
-
this.
|
|
1947
|
-
|
|
2027
|
+
while (true) {
|
|
2028
|
+
if (this._readyState === "closed") {
|
|
2029
|
+
this._queuedMessageEvents.length = 0;
|
|
2030
|
+
this._queuedMessageEventIndex = 0;
|
|
2031
|
+
return;
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
const pendingPeer =
|
|
2035
|
+
this._pc._operationsPending > 0
|
|
2036
|
+
? this._pc
|
|
2037
|
+
: this._pc._pairedPeer?._operationsPending > 0
|
|
2038
|
+
? this._pc._pairedPeer
|
|
2039
|
+
: null;
|
|
2040
|
+
if (pendingPeer) {
|
|
2041
|
+
pendingPeer._afterOperationsIdle(() => this._scheduleMessageEventFlush());
|
|
2042
|
+
return;
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
if (this._messageEventGateActive) return;
|
|
2046
|
+
if (this._messageConsumerGateActive && !this._hasEventConsumer("message")) return;
|
|
2047
|
+
if (this._messageConsumerGateActive) this._messageConsumerGateActive = false;
|
|
2048
|
+
|
|
2049
|
+
const event = this._queuedMessageEvents[this._queuedMessageEventIndex++];
|
|
2050
|
+
if (!event) {
|
|
2051
|
+
this._queuedMessageEventIndex = 0;
|
|
2052
|
+
return;
|
|
2053
|
+
}
|
|
2054
|
+
const handler = this._onmessage;
|
|
2055
|
+
const hasMessageListeners = this._hasMessageEventListeners();
|
|
2056
|
+
const yieldToTask = this._messageHandlerNeedsTaskYield;
|
|
2057
|
+
const directHandler = this._canUseDirectMessageHandler(
|
|
2058
|
+
handler,
|
|
2059
|
+
yieldToTask,
|
|
2060
|
+
hasMessageListeners,
|
|
2061
|
+
)
|
|
2062
|
+
? handler
|
|
2063
|
+
: null;
|
|
2064
|
+
this._dispatchMessageEvent(event, directHandler);
|
|
2065
|
+
this._markPairedDeliveryReceived(event);
|
|
2066
|
+
|
|
2067
|
+
if (this._queuedMessageEventIndex >= this._queuedMessageEvents.length) {
|
|
2068
|
+
this._queuedMessageEvents.length = 0;
|
|
2069
|
+
this._queuedMessageEventIndex = 0;
|
|
2070
|
+
return;
|
|
2071
|
+
}
|
|
2072
|
+
if (this._queuedMessageEventIndex > 1024) {
|
|
2073
|
+
this._queuedMessageEvents.splice(0, this._queuedMessageEventIndex);
|
|
2074
|
+
this._queuedMessageEventIndex = 0;
|
|
2075
|
+
}
|
|
2076
|
+
if (!this._canContinueMessageBatchInCurrentTask(handler, yieldToTask)) {
|
|
2077
|
+
this._scheduleMessageEventContinuation({ yieldToTask });
|
|
2078
|
+
return;
|
|
2079
|
+
}
|
|
1948
2080
|
}
|
|
1949
|
-
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
_dispatchNativeMessageBatch(events) {
|
|
2084
|
+
if (this._readyState === "closed") return true;
|
|
1950
2085
|
const pendingPeer =
|
|
1951
2086
|
this._pc._operationsPending > 0
|
|
1952
2087
|
? this._pc
|
|
1953
2088
|
: this._pc._pairedPeer?._operationsPending > 0
|
|
1954
2089
|
? this._pc._pairedPeer
|
|
1955
2090
|
: null;
|
|
1956
|
-
if (pendingPeer)
|
|
1957
|
-
|
|
1958
|
-
return;
|
|
1959
|
-
}
|
|
1960
|
-
if (this._messageEventGateActive) return;
|
|
1961
|
-
if (this._messageConsumerGateActive && !this._hasEventConsumer("message")) return;
|
|
2091
|
+
if (pendingPeer || this._messageEventGateActive) return false;
|
|
2092
|
+
if (this._messageConsumerGateActive && !this._hasEventConsumer("message")) return false;
|
|
1962
2093
|
if (this._messageConsumerGateActive) this._messageConsumerGateActive = false;
|
|
1963
2094
|
|
|
1964
|
-
const
|
|
1965
|
-
|
|
1966
|
-
this.
|
|
1967
|
-
|
|
2095
|
+
const handler = this._onmessage;
|
|
2096
|
+
const hasMessageListeners = this._hasMessageEventListeners();
|
|
2097
|
+
const yieldToTask = this._messageHandlerNeedsTaskYield;
|
|
2098
|
+
const directHandler = this._canUseDirectMessageHandler(
|
|
2099
|
+
handler,
|
|
2100
|
+
yieldToTask,
|
|
2101
|
+
hasMessageListeners,
|
|
2102
|
+
)
|
|
2103
|
+
? handler
|
|
2104
|
+
: null;
|
|
2105
|
+
if (!directHandler) return false;
|
|
2106
|
+
|
|
2107
|
+
const shouldCreateBlob = this._binaryType === "blob" && typeof Blob !== "undefined";
|
|
2108
|
+
for (let index = 0; index < events.length; index += 1) {
|
|
2109
|
+
const event = events[index];
|
|
2110
|
+
const messageEvent = makeMessageEvent("message", {
|
|
2111
|
+
data: event.binary && shouldCreateBlob ? new Blob([event.data]) : event.data,
|
|
2112
|
+
});
|
|
2113
|
+
messageEvent.target = this;
|
|
2114
|
+
messageEvent.currentTarget = this;
|
|
2115
|
+
directHandler.call(this, messageEvent);
|
|
2116
|
+
this._markPairedDeliveryReceived(event);
|
|
2117
|
+
if (this._readyState === "closed") return true;
|
|
2118
|
+
if (
|
|
2119
|
+
index + 1 < events.length &&
|
|
2120
|
+
!this._canContinueMessageBatchInCurrentTask(handler, yieldToTask)
|
|
2121
|
+
) {
|
|
2122
|
+
for (let remaining = index + 1; remaining < events.length; remaining += 1) {
|
|
2123
|
+
this._queuedMessageEvents.push(events[remaining]);
|
|
2124
|
+
}
|
|
2125
|
+
this._scheduleMessageEventContinuation({ yieldToTask: this._messageHandlerNeedsTaskYield });
|
|
2126
|
+
return true;
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
return true;
|
|
1968
2130
|
}
|
|
1969
2131
|
|
|
1970
2132
|
_gateMessageEventsAfterAnnouncement() {
|
|
@@ -1991,7 +2153,15 @@ class RTCDataChannel extends SimpleEventTarget {
|
|
|
1991
2153
|
}
|
|
1992
2154
|
|
|
1993
2155
|
_eventListenerAdded(type) {
|
|
1994
|
-
if (type
|
|
2156
|
+
if (type !== "message") return;
|
|
2157
|
+
this._messageEventListenerCount += 1;
|
|
2158
|
+
this._releaseMessageConsumerGate();
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
_eventListenerRemoved(type) {
|
|
2162
|
+
if (type === "message" && this._messageEventListenerCount > 0) {
|
|
2163
|
+
this._messageEventListenerCount -= 1;
|
|
2164
|
+
}
|
|
1995
2165
|
}
|
|
1996
2166
|
|
|
1997
2167
|
_convertMessage(event) {
|
|
@@ -2004,6 +2174,7 @@ class RTCDataChannel extends SimpleEventTarget {
|
|
|
2004
2174
|
}
|
|
2005
2175
|
|
|
2006
2176
|
function toUint8Array(data) {
|
|
2177
|
+
if (data instanceof Uint8Array) return data;
|
|
2007
2178
|
if (data instanceof ArrayBuffer) return new Uint8Array(data);
|
|
2008
2179
|
if (ArrayBuffer.isView(data)) {
|
|
2009
2180
|
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
@@ -2024,6 +2195,7 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
2024
2195
|
this._configuration = normalizedConfiguration;
|
|
2025
2196
|
this._channels = new Map();
|
|
2026
2197
|
this._usedDataChannelIds = new Map();
|
|
2198
|
+
this._dataChannelIdRefreshNeeded = false;
|
|
2027
2199
|
this._closed = false;
|
|
2028
2200
|
this._localDescription = null;
|
|
2029
2201
|
this._remoteDescription = null;
|
|
@@ -2049,6 +2221,8 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
2049
2221
|
this._lastCreatedOffer = null;
|
|
2050
2222
|
this._lastCreatedAnswer = null;
|
|
2051
2223
|
this._localDescriptionSetByApi = false;
|
|
2224
|
+
this._localDescriptionRefreshScheduled = false;
|
|
2225
|
+
this._nativeCandidateGatheringScheduled = false;
|
|
2052
2226
|
this._iceRestartPending = false;
|
|
2053
2227
|
this._pendingIceRestartCredentials = null;
|
|
2054
2228
|
this._jsOnlyIceRestartOfferPending = false;
|
|
@@ -2105,9 +2279,13 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
2105
2279
|
nativeCertificates = [createDefaultNativeCertificate()];
|
|
2106
2280
|
nativeConfiguration.certificates = nativeCertificates;
|
|
2107
2281
|
}
|
|
2108
|
-
const nativePeerConnection = new native.NativePeerConnection(nativeConfiguration, (event) =>
|
|
2109
|
-
|
|
2110
|
-
|
|
2282
|
+
const nativePeerConnection = new native.NativePeerConnection(nativeConfiguration, (event) => {
|
|
2283
|
+
if (Array.isArray(event)) {
|
|
2284
|
+
this._handleNativeEventBatch(event);
|
|
2285
|
+
} else {
|
|
2286
|
+
this._handleNativeEvent(event);
|
|
2287
|
+
}
|
|
2288
|
+
});
|
|
2111
2289
|
this._nativeCertificates = nativeCertificates;
|
|
2112
2290
|
this._native = nativePeerConnection;
|
|
2113
2291
|
return this._native;
|
|
@@ -2516,6 +2694,7 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
2516
2694
|
? new RTCSessionDescription(nativeDescription)
|
|
2517
2695
|
: null;
|
|
2518
2696
|
this._syncStatesFromNative();
|
|
2697
|
+
this._scheduleNativeCandidateGathering();
|
|
2519
2698
|
}
|
|
2520
2699
|
}
|
|
2521
2700
|
if (type === "offer") {
|
|
@@ -2557,6 +2736,7 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
2557
2736
|
normalized.type === "answer" &&
|
|
2558
2737
|
this._signalingState !== "have-local-offer" &&
|
|
2559
2738
|
this._signalingState !== "have-remote-pranswer" &&
|
|
2739
|
+
!this._jsOnlyIceRestartOfferPending &&
|
|
2560
2740
|
this._operationsPending === 0
|
|
2561
2741
|
) {
|
|
2562
2742
|
throw makeDOMException(
|
|
@@ -2592,7 +2772,8 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
2592
2772
|
if (
|
|
2593
2773
|
normalized.type === "answer" &&
|
|
2594
2774
|
this._signalingState !== "have-local-offer" &&
|
|
2595
|
-
this._signalingState !== "have-remote-pranswer"
|
|
2775
|
+
this._signalingState !== "have-remote-pranswer" &&
|
|
2776
|
+
!this._jsOnlyIceRestartOfferPending
|
|
2596
2777
|
) {
|
|
2597
2778
|
throw makeDOMException(
|
|
2598
2779
|
"Cannot set a remote answer in current signaling state",
|
|
@@ -2879,9 +3060,9 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
2879
3060
|
}
|
|
2880
3061
|
}, PEER_CONNECTION_NATIVE_CLOSE_DELAY_MS);
|
|
2881
3062
|
}
|
|
2882
|
-
|
|
3063
|
+
setImmediate(() => {
|
|
2883
3064
|
if (pairedPeer && !pairedPeer._closed) pairedPeer._handleRemotePeerClosed();
|
|
2884
|
-
}
|
|
3065
|
+
});
|
|
2885
3066
|
this._connectionState = "closed";
|
|
2886
3067
|
this._iceConnectionState = "closed";
|
|
2887
3068
|
this._deferredIceEvents = [];
|
|
@@ -3088,12 +3269,12 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
3088
3269
|
_scheduleSctpConnectedTransition() {
|
|
3089
3270
|
if (this._sctpConnectedTransitionScheduled) return;
|
|
3090
3271
|
this._sctpConnectedTransitionScheduled = true;
|
|
3091
|
-
|
|
3272
|
+
setImmediate(() => {
|
|
3092
3273
|
this._sctpConnectedTransitionScheduled = false;
|
|
3093
3274
|
if (this._closed || !this._sctpTransport) return;
|
|
3094
3275
|
this._sctpConnectedTransitionReady = true;
|
|
3095
3276
|
this._updateSctpTransport();
|
|
3096
|
-
}
|
|
3277
|
+
});
|
|
3097
3278
|
}
|
|
3098
3279
|
|
|
3099
3280
|
_shouldRepairConnectedState() {
|
|
@@ -3205,13 +3386,13 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
3205
3386
|
this._pairedPeer?._flushPendingRemoteCandidatesForNative();
|
|
3206
3387
|
this._scheduleDeferredIceEventFlush();
|
|
3207
3388
|
const callbacks = this._operationIdleCallbacks.splice(0);
|
|
3208
|
-
for (const callback of callbacks)
|
|
3389
|
+
for (const callback of callbacks) setImmediate(callback);
|
|
3209
3390
|
}
|
|
3210
3391
|
}
|
|
3211
3392
|
|
|
3212
3393
|
_afterOperationsIdle(callback) {
|
|
3213
3394
|
if (this._closed || this._operationsPending === 0) {
|
|
3214
|
-
|
|
3395
|
+
setImmediate(callback);
|
|
3215
3396
|
return;
|
|
3216
3397
|
}
|
|
3217
3398
|
this._operationIdleCallbacks.push(callback);
|
|
@@ -3225,7 +3406,7 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
3225
3406
|
_scheduleDeferredIceEventFlush() {
|
|
3226
3407
|
if (this._iceEventFlushScheduled || this._deferredIceEvents.length === 0) return;
|
|
3227
3408
|
this._iceEventFlushScheduled = true;
|
|
3228
|
-
|
|
3409
|
+
setImmediate(() => {
|
|
3229
3410
|
this._iceEventFlushScheduled = false;
|
|
3230
3411
|
if (this._closed) {
|
|
3231
3412
|
this._deferredIceEvents = [];
|
|
@@ -3245,7 +3426,7 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
3245
3426
|
}
|
|
3246
3427
|
if (this._deferredIceEvents.length) this._scheduleDeferredIceEventFlush();
|
|
3247
3428
|
else this._flushIceEventIdleCallbacks();
|
|
3248
|
-
}
|
|
3429
|
+
});
|
|
3249
3430
|
}
|
|
3250
3431
|
|
|
3251
3432
|
_hasPendingDeferredIceEvents() {
|
|
@@ -3258,7 +3439,7 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
3258
3439
|
|
|
3259
3440
|
_afterDeferredIceEventsFlushed(callback) {
|
|
3260
3441
|
if (this._closed || !this._hasPendingDeferredIceEvents()) {
|
|
3261
|
-
|
|
3442
|
+
setImmediate(callback);
|
|
3262
3443
|
return;
|
|
3263
3444
|
}
|
|
3264
3445
|
this._iceEventIdleCallbacks.push(callback);
|
|
@@ -3268,7 +3449,7 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
3268
3449
|
_flushIceEventIdleCallbacks() {
|
|
3269
3450
|
if (this._hasPendingDeferredIceEvents()) return;
|
|
3270
3451
|
const callbacks = this._iceEventIdleCallbacks.splice(0);
|
|
3271
|
-
for (const callback of callbacks)
|
|
3452
|
+
for (const callback of callbacks) setImmediate(callback);
|
|
3272
3453
|
}
|
|
3273
3454
|
|
|
3274
3455
|
_queueSyntheticIceRestartGathering() {
|
|
@@ -3285,7 +3466,7 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
3285
3466
|
_scheduleSctpTransportUpdate() {
|
|
3286
3467
|
if (this._sctpTransportUpdateScheduled) return;
|
|
3287
3468
|
this._sctpTransportUpdateScheduled = true;
|
|
3288
|
-
|
|
3469
|
+
setImmediate(() => {
|
|
3289
3470
|
this._sctpTransportUpdateScheduled = false;
|
|
3290
3471
|
if (
|
|
3291
3472
|
!this._closed &&
|
|
@@ -3297,7 +3478,7 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
3297
3478
|
this._iceConnectionState = this._native.iceConnectionState;
|
|
3298
3479
|
}
|
|
3299
3480
|
this._updateSctpTransport();
|
|
3300
|
-
}
|
|
3481
|
+
});
|
|
3301
3482
|
}
|
|
3302
3483
|
|
|
3303
3484
|
_scheduleSctpConnectPollIfNeeded() {
|
|
@@ -3771,6 +3952,7 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
3771
3952
|
? new RTCSessionDescription(nativeDescription)
|
|
3772
3953
|
: null;
|
|
3773
3954
|
this._syncStatesFromNative();
|
|
3955
|
+
this._scheduleNativeCandidateGathering();
|
|
3774
3956
|
for (const candidate of this._pendingIce.splice(0)) await this.addIceCandidate(candidate);
|
|
3775
3957
|
this._flushPendingRemoteCandidatesForNative();
|
|
3776
3958
|
await nextTask();
|
|
@@ -3814,11 +3996,23 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
3814
3996
|
await nextTask();
|
|
3815
3997
|
}
|
|
3816
3998
|
|
|
3817
|
-
|
|
3999
|
+
_scheduleNativeCandidateGathering() {
|
|
4000
|
+
if (this._closed || !this._native || !hasDataMediaSection(this._localDescription)) return;
|
|
4001
|
+
if (this._nativeCandidateGatheringScheduled) return;
|
|
4002
|
+
this._nativeCandidateGatheringScheduled = true;
|
|
4003
|
+
setImmediate(() => {
|
|
4004
|
+
this._nativeCandidateGatheringScheduled = false;
|
|
4005
|
+
if (this._closed || !this._native || !hasDataMediaSection(this._localDescription)) return;
|
|
4006
|
+
try {
|
|
4007
|
+
this._native.gatherLocalCandidates();
|
|
4008
|
+
} catch {
|
|
4009
|
+
// Gathering may already be complete or the native transport may be closing.
|
|
4010
|
+
}
|
|
4011
|
+
});
|
|
4012
|
+
}
|
|
4013
|
+
|
|
4014
|
+
_refreshLocalDescriptionFromNative() {
|
|
3818
4015
|
if (this._closed || !this._localDescription) return;
|
|
3819
|
-
if (this._iceGatheringState !== "complete") {
|
|
3820
|
-
await delay(50);
|
|
3821
|
-
}
|
|
3822
4016
|
if (!this._native) return;
|
|
3823
4017
|
const nativeDescription = this._native.localDescription();
|
|
3824
4018
|
if (nativeDescription) {
|
|
@@ -3828,12 +4022,27 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
3828
4022
|
this._syncStatesFromNative();
|
|
3829
4023
|
}
|
|
3830
4024
|
|
|
4025
|
+
_refreshLocalDescriptionAfterGatheringWindow() {
|
|
4026
|
+
this._refreshLocalDescriptionFromNative();
|
|
4027
|
+
if (this._closed || !this._localDescription || this._iceGatheringState === "complete") return;
|
|
4028
|
+
if (this._localDescriptionRefreshScheduled) return;
|
|
4029
|
+
this._localDescriptionRefreshScheduled = true;
|
|
4030
|
+
setTimeout(() => {
|
|
4031
|
+
this._localDescriptionRefreshScheduled = false;
|
|
4032
|
+
this._refreshLocalDescriptionFromNative();
|
|
4033
|
+
}, 50);
|
|
4034
|
+
}
|
|
4035
|
+
|
|
3831
4036
|
_syncStatesFromNative() {
|
|
3832
4037
|
if (this._closed) return;
|
|
3833
4038
|
if (!this._native) return;
|
|
3834
4039
|
this._connectionState = this._native.connectionState;
|
|
3835
4040
|
this._iceConnectionState = this._native.iceConnectionState;
|
|
3836
|
-
this.
|
|
4041
|
+
if (this._jsOnlyIceRestartOfferPending) {
|
|
4042
|
+
this._signalingState = "have-local-offer";
|
|
4043
|
+
} else {
|
|
4044
|
+
this._signalingState = this._native.signalingState;
|
|
4045
|
+
}
|
|
3837
4046
|
this._refreshIceRole();
|
|
3838
4047
|
this._updateSctpTransport();
|
|
3839
4048
|
}
|
|
@@ -3936,6 +4145,10 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
3936
4145
|
this._closeChannelsOnPeerFailure();
|
|
3937
4146
|
break;
|
|
3938
4147
|
case "signalingstatechange":
|
|
4148
|
+
if (this._jsOnlyIceRestartOfferPending) {
|
|
4149
|
+
this._signalingState = "have-local-offer";
|
|
4150
|
+
break;
|
|
4151
|
+
}
|
|
3939
4152
|
this._signalingState = event.state;
|
|
3940
4153
|
if (this._suppressNextNativeSignalingState === event.state) {
|
|
3941
4154
|
this._suppressNextNativeSignalingState = null;
|
|
@@ -3946,6 +4159,43 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
3946
4159
|
}
|
|
3947
4160
|
}
|
|
3948
4161
|
|
|
4162
|
+
_handleNativeEventBatch(events) {
|
|
4163
|
+
if (!events.length) return;
|
|
4164
|
+
const first = events[0];
|
|
4165
|
+
if (first?.target !== "datachannel" || first.type !== "message" || first.channelId == null) {
|
|
4166
|
+
for (const event of events) this._handleNativeEvent(event);
|
|
4167
|
+
return;
|
|
4168
|
+
}
|
|
4169
|
+
|
|
4170
|
+
const channelId = first.channelId;
|
|
4171
|
+
for (let index = 1; index < events.length; index += 1) {
|
|
4172
|
+
const event = events[index];
|
|
4173
|
+
if (
|
|
4174
|
+
event?.target !== "datachannel" ||
|
|
4175
|
+
event.type !== "message" ||
|
|
4176
|
+
event.channelId !== channelId
|
|
4177
|
+
) {
|
|
4178
|
+
for (const item of events) this._handleNativeEvent(item);
|
|
4179
|
+
return;
|
|
4180
|
+
}
|
|
4181
|
+
}
|
|
4182
|
+
|
|
4183
|
+
const channel = this._channels.get(channelId);
|
|
4184
|
+
if (channel) {
|
|
4185
|
+
if (channel._announcementPending || channel._nativeEventDrainActive) {
|
|
4186
|
+
for (const event of events) channel._queuedNativeEvents.push(event);
|
|
4187
|
+
return;
|
|
4188
|
+
}
|
|
4189
|
+
if (channel._dispatchNativeMessageBatch(events)) return;
|
|
4190
|
+
channel._queueMessageEvents(events);
|
|
4191
|
+
return;
|
|
4192
|
+
}
|
|
4193
|
+
|
|
4194
|
+
const pending = this._pendingNativeDataChannelEvents.get(channelId) || [];
|
|
4195
|
+
for (const event of events) pending.push(event);
|
|
4196
|
+
this._pendingNativeDataChannelEvents.set(channelId, pending);
|
|
4197
|
+
}
|
|
4198
|
+
|
|
3949
4199
|
_registerDataChannelId(channel, id = channel._effectiveId()) {
|
|
3950
4200
|
if (id == null) return;
|
|
3951
4201
|
if (channel._registeredDataChannelId === id) return;
|
|
@@ -3980,10 +4230,22 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
3980
4230
|
}
|
|
3981
4231
|
|
|
3982
4232
|
_isDataChannelIdInUse(id) {
|
|
3983
|
-
|
|
3984
|
-
|
|
4233
|
+
let channel = this._usedDataChannelIds.get(id);
|
|
4234
|
+
if (channel && channel.readyState !== "closed") return true;
|
|
4235
|
+
if (!this._dataChannelIdRefreshNeeded) return false;
|
|
4236
|
+
|
|
4237
|
+
let refreshStillNeeded = false;
|
|
4238
|
+
for (const currentChannel of this._channels.values()) {
|
|
4239
|
+
if (currentChannel.readyState === "closed") continue;
|
|
4240
|
+
const effectiveId = currentChannel._effectiveId();
|
|
4241
|
+
if (effectiveId == null) {
|
|
4242
|
+
refreshStillNeeded = true;
|
|
4243
|
+
continue;
|
|
4244
|
+
}
|
|
4245
|
+
this._registerDataChannelId(currentChannel, effectiveId);
|
|
3985
4246
|
}
|
|
3986
|
-
|
|
4247
|
+
this._dataChannelIdRefreshNeeded = refreshStillNeeded;
|
|
4248
|
+
channel = this._usedDataChannelIds.get(id);
|
|
3987
4249
|
return Boolean(channel && channel.readyState !== "closed");
|
|
3988
4250
|
}
|
|
3989
4251
|
|
|
@@ -4069,6 +4331,7 @@ class RTCPeerConnection extends SimpleEventTarget {
|
|
|
4069
4331
|
const events = this._pendingDataChannelEvents.splice(0);
|
|
4070
4332
|
for (const event of events) {
|
|
4071
4333
|
if (this._closed) return;
|
|
4334
|
+
event.channel._gateMessageEventsUntilConsumer();
|
|
4072
4335
|
this.dispatchEvent(event);
|
|
4073
4336
|
event.channel._announcementPending = false;
|
|
4074
4337
|
event.channel._announceOpenAfterDataChannelEvent();
|