You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
347 lines
13 KiB
347 lines
13 KiB
/*
|
|
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree.
|
|
*/
|
|
/* eslint-env node */
|
|
'use strict';
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
|
|
exports.shimRTCIceCandidate = shimRTCIceCandidate;
|
|
exports.shimMaxMessageSize = shimMaxMessageSize;
|
|
exports.shimSendThrowTypeError = shimSendThrowTypeError;
|
|
exports.shimConnectionState = shimConnectionState;
|
|
exports.removeAllowExtmapMixed = removeAllowExtmapMixed;
|
|
|
|
var _sdp = require('sdp');
|
|
|
|
var _sdp2 = _interopRequireDefault(_sdp);
|
|
|
|
var _utils = require('./utils');
|
|
|
|
var utils = _interopRequireWildcard(_utils);
|
|
|
|
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
function shimRTCIceCandidate(window) {
|
|
// foundation is arbitrarily chosen as an indicator for full support for
|
|
// https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface
|
|
if (!window.RTCIceCandidate || window.RTCIceCandidate && 'foundation' in window.RTCIceCandidate.prototype) {
|
|
return;
|
|
}
|
|
|
|
var NativeRTCIceCandidate = window.RTCIceCandidate;
|
|
window.RTCIceCandidate = function RTCIceCandidate(args) {
|
|
// Remove the a= which shouldn't be part of the candidate string.
|
|
if ((typeof args === 'undefined' ? 'undefined' : _typeof(args)) === 'object' && args.candidate && args.candidate.indexOf('a=') === 0) {
|
|
args = JSON.parse(JSON.stringify(args));
|
|
args.candidate = args.candidate.substr(2);
|
|
}
|
|
|
|
if (args.candidate && args.candidate.length) {
|
|
// Augment the native candidate with the parsed fields.
|
|
var nativeCandidate = new NativeRTCIceCandidate(args);
|
|
var parsedCandidate = _sdp2.default.parseCandidate(args.candidate);
|
|
var augmentedCandidate = Object.assign(nativeCandidate, parsedCandidate);
|
|
|
|
// Add a serializer that does not serialize the extra attributes.
|
|
augmentedCandidate.toJSON = function toJSON() {
|
|
return {
|
|
candidate: augmentedCandidate.candidate,
|
|
sdpMid: augmentedCandidate.sdpMid,
|
|
sdpMLineIndex: augmentedCandidate.sdpMLineIndex,
|
|
usernameFragment: augmentedCandidate.usernameFragment
|
|
};
|
|
};
|
|
return augmentedCandidate;
|
|
}
|
|
return new NativeRTCIceCandidate(args);
|
|
};
|
|
window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype;
|
|
|
|
// Hook up the augmented candidate in onicecandidate and
|
|
// addEventListener('icecandidate', ...)
|
|
utils.wrapPeerConnectionEvent(window, 'icecandidate', function (e) {
|
|
if (e.candidate) {
|
|
Object.defineProperty(e, 'candidate', {
|
|
value: new window.RTCIceCandidate(e.candidate),
|
|
writable: 'false'
|
|
});
|
|
}
|
|
return e;
|
|
});
|
|
}
|
|
|
|
function shimMaxMessageSize(window) {
|
|
if (!window.RTCPeerConnection) {
|
|
return;
|
|
}
|
|
var browserDetails = utils.detectBrowser(window);
|
|
|
|
if (!('sctp' in window.RTCPeerConnection.prototype)) {
|
|
Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', {
|
|
get: function get() {
|
|
return typeof this._sctp === 'undefined' ? null : this._sctp;
|
|
}
|
|
});
|
|
}
|
|
|
|
var sctpInDescription = function sctpInDescription(description) {
|
|
if (!description || !description.sdp) {
|
|
return false;
|
|
}
|
|
var sections = _sdp2.default.splitSections(description.sdp);
|
|
sections.shift();
|
|
return sections.some(function (mediaSection) {
|
|
var mLine = _sdp2.default.parseMLine(mediaSection);
|
|
return mLine && mLine.kind === 'application' && mLine.protocol.indexOf('SCTP') !== -1;
|
|
});
|
|
};
|
|
|
|
var getRemoteFirefoxVersion = function getRemoteFirefoxVersion(description) {
|
|
// TODO: Is there a better solution for detecting Firefox?
|
|
var match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/);
|
|
if (match === null || match.length < 2) {
|
|
return -1;
|
|
}
|
|
var version = parseInt(match[1], 10);
|
|
// Test for NaN (yes, this is ugly)
|
|
return version !== version ? -1 : version;
|
|
};
|
|
|
|
var getCanSendMaxMessageSize = function getCanSendMaxMessageSize(remoteIsFirefox) {
|
|
// Every implementation we know can send at least 64 KiB.
|
|
// Note: Although Chrome is technically able to send up to 256 KiB, the
|
|
// data does not reach the other peer reliably.
|
|
// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419
|
|
var canSendMaxMessageSize = 65536;
|
|
if (browserDetails.browser === 'firefox') {
|
|
if (browserDetails.version < 57) {
|
|
if (remoteIsFirefox === -1) {
|
|
// FF < 57 will send in 16 KiB chunks using the deprecated PPID
|
|
// fragmentation.
|
|
canSendMaxMessageSize = 16384;
|
|
} else {
|
|
// However, other FF (and RAWRTC) can reassemble PPID-fragmented
|
|
// messages. Thus, supporting ~2 GiB when sending.
|
|
canSendMaxMessageSize = 2147483637;
|
|
}
|
|
} else if (browserDetails.version < 60) {
|
|
// Currently, all FF >= 57 will reset the remote maximum message size
|
|
// to the default value when a data channel is created at a later
|
|
// stage. :(
|
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
|
|
canSendMaxMessageSize = browserDetails.version === 57 ? 65535 : 65536;
|
|
} else {
|
|
// FF >= 60 supports sending ~2 GiB
|
|
canSendMaxMessageSize = 2147483637;
|
|
}
|
|
}
|
|
return canSendMaxMessageSize;
|
|
};
|
|
|
|
var getMaxMessageSize = function getMaxMessageSize(description, remoteIsFirefox) {
|
|
// Note: 65536 bytes is the default value from the SDP spec. Also,
|
|
// every implementation we know supports receiving 65536 bytes.
|
|
var maxMessageSize = 65536;
|
|
|
|
// FF 57 has a slightly incorrect default remote max message size, so
|
|
// we need to adjust it here to avoid a failure when sending.
|
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697
|
|
if (browserDetails.browser === 'firefox' && browserDetails.version === 57) {
|
|
maxMessageSize = 65535;
|
|
}
|
|
|
|
var match = _sdp2.default.matchPrefix(description.sdp, 'a=max-message-size:');
|
|
if (match.length > 0) {
|
|
maxMessageSize = parseInt(match[0].substr(19), 10);
|
|
} else if (browserDetails.browser === 'firefox' && remoteIsFirefox !== -1) {
|
|
// If the maximum message size is not present in the remote SDP and
|
|
// both local and remote are Firefox, the remote peer can receive
|
|
// ~2 GiB.
|
|
maxMessageSize = 2147483637;
|
|
}
|
|
return maxMessageSize;
|
|
};
|
|
|
|
var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription;
|
|
window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() {
|
|
this._sctp = null;
|
|
// Chrome decided to not expose .sctp in plan-b mode.
|
|
// As usual, adapter.js has to do an 'ugly worakaround'
|
|
// to cover up the mess.
|
|
if (browserDetails.browser === 'chrome' && browserDetails.version >= 76) {
|
|
var _getConfiguration = this.getConfiguration(),
|
|
sdpSemantics = _getConfiguration.sdpSemantics;
|
|
|
|
if (sdpSemantics === 'plan-b') {
|
|
Object.defineProperty(this, 'sctp', {
|
|
get: function get() {
|
|
return typeof this._sctp === 'undefined' ? null : this._sctp;
|
|
},
|
|
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
}
|
|
}
|
|
|
|
if (sctpInDescription(arguments[0])) {
|
|
// Check if the remote is FF.
|
|
var isFirefox = getRemoteFirefoxVersion(arguments[0]);
|
|
|
|
// Get the maximum message size the local peer is capable of sending
|
|
var canSendMMS = getCanSendMaxMessageSize(isFirefox);
|
|
|
|
// Get the maximum message size of the remote peer.
|
|
var remoteMMS = getMaxMessageSize(arguments[0], isFirefox);
|
|
|
|
// Determine final maximum message size
|
|
var maxMessageSize = void 0;
|
|
if (canSendMMS === 0 && remoteMMS === 0) {
|
|
maxMessageSize = Number.POSITIVE_INFINITY;
|
|
} else if (canSendMMS === 0 || remoteMMS === 0) {
|
|
maxMessageSize = Math.max(canSendMMS, remoteMMS);
|
|
} else {
|
|
maxMessageSize = Math.min(canSendMMS, remoteMMS);
|
|
}
|
|
|
|
// Create a dummy RTCSctpTransport object and the 'maxMessageSize'
|
|
// attribute.
|
|
var sctp = {};
|
|
Object.defineProperty(sctp, 'maxMessageSize', {
|
|
get: function get() {
|
|
return maxMessageSize;
|
|
}
|
|
});
|
|
this._sctp = sctp;
|
|
}
|
|
|
|
return origSetRemoteDescription.apply(this, arguments);
|
|
};
|
|
}
|
|
|
|
function shimSendThrowTypeError(window) {
|
|
if (!(window.RTCPeerConnection && 'createDataChannel' in window.RTCPeerConnection.prototype)) {
|
|
return;
|
|
}
|
|
|
|
// Note: Although Firefox >= 57 has a native implementation, the maximum
|
|
// message size can be reset for all data channels at a later stage.
|
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
|
|
|
|
function wrapDcSend(dc, pc) {
|
|
var origDataChannelSend = dc.send;
|
|
dc.send = function send() {
|
|
var data = arguments[0];
|
|
var length = data.length || data.size || data.byteLength;
|
|
if (dc.readyState === 'open' && pc.sctp && length > pc.sctp.maxMessageSize) {
|
|
throw new TypeError('Message too large (can send a maximum of ' + pc.sctp.maxMessageSize + ' bytes)');
|
|
}
|
|
return origDataChannelSend.apply(dc, arguments);
|
|
};
|
|
}
|
|
var origCreateDataChannel = window.RTCPeerConnection.prototype.createDataChannel;
|
|
window.RTCPeerConnection.prototype.createDataChannel = function createDataChannel() {
|
|
var dataChannel = origCreateDataChannel.apply(this, arguments);
|
|
wrapDcSend(dataChannel, this);
|
|
return dataChannel;
|
|
};
|
|
utils.wrapPeerConnectionEvent(window, 'datachannel', function (e) {
|
|
wrapDcSend(e.channel, e.target);
|
|
return e;
|
|
});
|
|
}
|
|
|
|
/* shims RTCConnectionState by pretending it is the same as iceConnectionState.
|
|
* See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12
|
|
* for why this is a valid hack in Chrome. In Firefox it is slightly incorrect
|
|
* since DTLS failures would be hidden. See
|
|
* https://bugzilla.mozilla.org/show_bug.cgi?id=1265827
|
|
* for the Firefox tracking bug.
|
|
*/
|
|
function shimConnectionState(window) {
|
|
if (!window.RTCPeerConnection || 'connectionState' in window.RTCPeerConnection.prototype) {
|
|
return;
|
|
}
|
|
var proto = window.RTCPeerConnection.prototype;
|
|
Object.defineProperty(proto, 'connectionState', {
|
|
get: function get() {
|
|
return {
|
|
completed: 'connected',
|
|
checking: 'connecting'
|
|
}[this.iceConnectionState] || this.iceConnectionState;
|
|
},
|
|
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
Object.defineProperty(proto, 'onconnectionstatechange', {
|
|
get: function get() {
|
|
return this._onconnectionstatechange || null;
|
|
},
|
|
set: function set(cb) {
|
|
if (this._onconnectionstatechange) {
|
|
this.removeEventListener('connectionstatechange', this._onconnectionstatechange);
|
|
delete this._onconnectionstatechange;
|
|
}
|
|
if (cb) {
|
|
this.addEventListener('connectionstatechange', this._onconnectionstatechange = cb);
|
|
}
|
|
},
|
|
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
|
|
['setLocalDescription', 'setRemoteDescription'].forEach(function (method) {
|
|
var origMethod = proto[method];
|
|
proto[method] = function () {
|
|
if (!this._connectionstatechangepoly) {
|
|
this._connectionstatechangepoly = function (e) {
|
|
var pc = e.target;
|
|
if (pc._lastConnectionState !== pc.connectionState) {
|
|
pc._lastConnectionState = pc.connectionState;
|
|
var newEvent = new Event('connectionstatechange', e);
|
|
pc.dispatchEvent(newEvent);
|
|
}
|
|
return e;
|
|
};
|
|
this.addEventListener('iceconnectionstatechange', this._connectionstatechangepoly);
|
|
}
|
|
return origMethod.apply(this, arguments);
|
|
};
|
|
});
|
|
}
|
|
|
|
function removeAllowExtmapMixed(window) {
|
|
/* remove a=extmap-allow-mixed for webrtc.org < M71 */
|
|
if (!window.RTCPeerConnection) {
|
|
return;
|
|
}
|
|
var browserDetails = utils.detectBrowser(window);
|
|
if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {
|
|
return;
|
|
}
|
|
if (browserDetails.browser === 'safari' && browserDetails.version >= 605) {
|
|
return;
|
|
}
|
|
var nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription;
|
|
window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription(desc) {
|
|
if (desc && desc.sdp && desc.sdp.indexOf('\na=extmap-allow-mixed') !== -1) {
|
|
desc.sdp = desc.sdp.split('\n').filter(function (line) {
|
|
return line.trim() !== 'a=extmap-allow-mixed';
|
|
}).join('\n');
|
|
}
|
|
return nativeSRD.apply(this, arguments);
|
|
};
|
|
}
|
|
|