зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1876865 - Add media.peerconnection.sdp.quirk.duplicate_fingerprint.allowlist. r=bwc,webidl,smaug
Differential Revision: https://phabricator.services.mozilla.com/D217016
This commit is contained in:
Родитель
c4707fa06b
Коммит
d978eb6a0b
|
@ -1029,10 +1029,33 @@ export class RTCPeerConnection {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_maybeDuplicateFingerprints(sdp) {
|
||||||
|
if (this._pc.duplicateFingerprintQuirk) {
|
||||||
|
// hack to put a=fingerprint under all m= lines
|
||||||
|
const lines = sdp.split("\n");
|
||||||
|
const fingerprint = lines.find(line => line.startsWith("a=fingerprint"));
|
||||||
|
if (fingerprint) {
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
if (lines[i].startsWith("m=")) {
|
||||||
|
let j = i + 1;
|
||||||
|
while (j < lines.length && lines[j].startsWith("c=")) {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
lines.splice(j, 0, fingerprint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sdp = lines.join("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sdp;
|
||||||
|
}
|
||||||
|
|
||||||
_createOffer(options) {
|
_createOffer(options) {
|
||||||
this._checkClosed();
|
this._checkClosed();
|
||||||
this._ensureTransceiversForOfferToReceive(options);
|
this._ensureTransceiversForOfferToReceive(options);
|
||||||
return this._chain(() => this._createAnOffer(options));
|
return this._chain(async () =>
|
||||||
|
Cu.cloneInto(await this._createAnOffer(options), this._win)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _createAnOffer(options = {}) {
|
async _createAnOffer(options = {}) {
|
||||||
|
@ -1057,11 +1080,12 @@ export class RTCPeerConnection {
|
||||||
this._onCreateOfferFailure = reject;
|
this._onCreateOfferFailure = reject;
|
||||||
this._pc.createOffer(options);
|
this._pc.createOffer(options);
|
||||||
});
|
});
|
||||||
|
sdp = this._maybeDuplicateFingerprints(sdp);
|
||||||
if (haveAssertion) {
|
if (haveAssertion) {
|
||||||
await haveAssertion;
|
await haveAssertion;
|
||||||
sdp = this._localIdp.addIdentityAttribute(sdp);
|
sdp = this._localIdp.addIdentityAttribute(sdp);
|
||||||
}
|
}
|
||||||
return Cu.cloneInto({ type: "offer", sdp }, this._win);
|
return { type: "offer", sdp };
|
||||||
}
|
}
|
||||||
|
|
||||||
createAnswer(optionsOrOnSucc, onErr) {
|
createAnswer(optionsOrOnSucc, onErr) {
|
||||||
|
@ -1074,7 +1098,9 @@ export class RTCPeerConnection {
|
||||||
|
|
||||||
_createAnswer() {
|
_createAnswer() {
|
||||||
this._checkClosed();
|
this._checkClosed();
|
||||||
return this._chain(() => this._createAnAnswer());
|
return this._chain(async () =>
|
||||||
|
Cu.cloneInto(await this._createAnAnswer(), this._win)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _createAnAnswer() {
|
async _createAnAnswer() {
|
||||||
|
@ -1095,11 +1121,12 @@ export class RTCPeerConnection {
|
||||||
this._onCreateAnswerFailure = reject;
|
this._onCreateAnswerFailure = reject;
|
||||||
this._pc.createAnswer();
|
this._pc.createAnswer();
|
||||||
});
|
});
|
||||||
|
sdp = this._maybeDuplicateFingerprints(sdp);
|
||||||
if (haveAssertion) {
|
if (haveAssertion) {
|
||||||
await haveAssertion;
|
await haveAssertion;
|
||||||
sdp = this._localIdp.addIdentityAttribute(sdp);
|
sdp = this._localIdp.addIdentityAttribute(sdp);
|
||||||
}
|
}
|
||||||
return Cu.cloneInto({ type: "answer", sdp }, this._win);
|
return { type: "answer", sdp };
|
||||||
}
|
}
|
||||||
|
|
||||||
async _getPermission() {
|
async _getPermission() {
|
||||||
|
@ -1175,9 +1202,9 @@ export class RTCPeerConnection {
|
||||||
}
|
}
|
||||||
if (!sdp) {
|
if (!sdp) {
|
||||||
if (type == "offer") {
|
if (type == "offer") {
|
||||||
await this._createAnOffer();
|
sdp = (await this._createAnOffer()).sdp;
|
||||||
} else if (type == "answer") {
|
} else if (type == "answer") {
|
||||||
await this._createAnAnswer();
|
sdp = (await this._createAnAnswer()).sdp;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this._sanityCheckSdp(sdp);
|
this._sanityCheckSdp(sdp);
|
||||||
|
|
|
@ -398,6 +398,9 @@ PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal)
|
||||||
|
|
||||||
mRtxIsAllowed = !HostnameInPref(
|
mRtxIsAllowed = !HostnameInPref(
|
||||||
"media.peerconnection.video.use_rtx.blocklist", mHostname);
|
"media.peerconnection.video.use_rtx.blocklist", mHostname);
|
||||||
|
mDuplicateFingerprintQuirk = HostnameInPref(
|
||||||
|
"media.peerconnection.sdp.quirk.duplicate_fingerprint.allowlist",
|
||||||
|
mHostname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -346,6 +346,8 @@ class PeerConnectionImpl final
|
||||||
PrincipalPrivacy::Private;
|
PrincipalPrivacy::Private;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DuplicateFingerprintQuirk() { return mDuplicateFingerprintQuirk; }
|
||||||
|
|
||||||
NS_IMETHODIMP GetFingerprint(char** fingerprint);
|
NS_IMETHODIMP GetFingerprint(char** fingerprint);
|
||||||
void GetFingerprint(nsAString& fingerprint) {
|
void GetFingerprint(nsAString& fingerprint) {
|
||||||
char* tmp;
|
char* tmp;
|
||||||
|
@ -869,6 +871,8 @@ class PeerConnectionImpl final
|
||||||
// See Bug 1642419, this can be removed when all sites are working with RTX.
|
// See Bug 1642419, this can be removed when all sites are working with RTX.
|
||||||
bool mRtxIsAllowed = true;
|
bool mRtxIsAllowed = true;
|
||||||
|
|
||||||
|
bool mDuplicateFingerprintQuirk = false;
|
||||||
|
|
||||||
nsTArray<RefPtr<Operation>> mOperations;
|
nsTArray<RefPtr<Operation>> mOperations;
|
||||||
bool mChainingOperation = false;
|
bool mChainingOperation = false;
|
||||||
bool mUpdateNegotiationNeededFlagOnEmptyChain = false;
|
bool mUpdateNegotiationNeededFlagOnEmptyChain = false;
|
||||||
|
|
|
@ -19,59 +19,117 @@
|
||||||
is(d1.sdp, d2.sdp, msg + " — and same sdp");
|
is(d1.sdp, d2.sdp, msg + " — and same sdp");
|
||||||
}
|
}
|
||||||
|
|
||||||
runNetworkTest(async () => {
|
const addIce = (pc, can, failed) => can && pc.addIceCandidate(can).catch(failed);
|
||||||
await SpecialPowers.pushPrefEnv({
|
|
||||||
set: [['media.peerconnection.description.legacy.enabled', true]]
|
|
||||||
});
|
|
||||||
|
|
||||||
const pc1 = new RTCPeerConnection();
|
const tests = [
|
||||||
const pc2 = new RTCPeerConnection();
|
async function checkLegacyDescriptions() {
|
||||||
|
await withPrefs([["media.peerconnection.description.legacy.enabled", true]],
|
||||||
|
async () => {
|
||||||
|
const pc1 = new RTCPeerConnection();
|
||||||
|
const pc2 = new RTCPeerConnection();
|
||||||
|
|
||||||
const add = (pc, can, failed) => can && pc.addIceCandidate(can).catch(failed);
|
pc1.onicecandidate = e => addIce(pc2, e.candidate, generateErrorCallback());
|
||||||
pc1.onicecandidate = e => add(pc2, e.candidate, generateErrorCallback());
|
pc2.onicecandidate = e => addIce(pc1, e.candidate, generateErrorCallback());
|
||||||
pc2.onicecandidate = e => add(pc1, e.candidate, generateErrorCallback());
|
|
||||||
|
|
||||||
const v1 = createMediaElement('video', 'v1');
|
const v1 = createMediaElement('video', 'v1');
|
||||||
const v2 = createMediaElement('video', 'v2');
|
const v2 = createMediaElement('video', 'v2');
|
||||||
|
|
||||||
const stream = await navigator.mediaDevices.getUserMedia({
|
const stream = await navigator.mediaDevices.getUserMedia({
|
||||||
video: true, audio: true
|
video: true, audio: true
|
||||||
});
|
});
|
||||||
v1.srcObject = stream;
|
v1.srcObject = stream;
|
||||||
for (const track of stream.getTracks()) {
|
for (const track of stream.getTracks()) {
|
||||||
pc1.addTrack(track, stream);
|
pc1.addTrack(track, stream);
|
||||||
|
}
|
||||||
|
await pc1.setLocalDescription();
|
||||||
|
is(pc1.currentLocalDescription, null, "pc1 currentLocalDescription is null");
|
||||||
|
ok(pc1.pendingLocalDescription, "pc1 pendingLocalDescription is set");
|
||||||
|
ok(pc1.localDescription, "pc1 localDescription is set");
|
||||||
|
isSimilarDescription(pc1.pendingLocalDescription, pc1.pendingLocalDescription, "pendingLocalDescription");
|
||||||
|
isSimilarDescription(pc1.localDescription, pc1.localDescription, "localDescription");
|
||||||
|
isSimilarDescription(pc1.localDescription, pc1.pendingLocalDescription, "local and pending");
|
||||||
|
|
||||||
|
await pc2.setRemoteDescription(pc1.localDescription);
|
||||||
|
is(pc2.currentRemoteDescription, null, "pc2 currentRemoteDescription is null");
|
||||||
|
ok(pc2.pendingRemoteDescription, "pc2 pendingRemoteDescription is set");
|
||||||
|
ok(pc2.remoteDescription, "pc2 remoteDescription is set");
|
||||||
|
isSimilarDescription(pc2.pendingRemoteDescription, pc2.pendingRemoteDescription, "pendingRemoteDescription");
|
||||||
|
isSimilarDescription(pc2.remoteDescription, pc2.remoteDescription, "remoteDescription");
|
||||||
|
isSimilarDescription(pc2.remoteDescription, pc2.pendingRemoteDescription, "remote and pending");
|
||||||
|
|
||||||
|
await pc2.setLocalDescription();
|
||||||
|
ok(pc2.currentLocalDescription, "pc2 currentLocalDescription is set");
|
||||||
|
is(pc2.pendingLocalDescription, null, "pc2 pendingLocalDescription is null");
|
||||||
|
ok(pc2.localDescription, "pc2 localDescription is set");
|
||||||
|
isSimilarDescription(pc2.currentLocalDescription, pc2.currentLocalDescription, "currentLocalDescription");
|
||||||
|
isSimilarDescription(pc2.localDescription, pc2.localDescription, "localDescription");
|
||||||
|
isSimilarDescription(pc2.localDescription, pc2.currentLocalDescription, "local and current");
|
||||||
|
|
||||||
|
await pc1.setRemoteDescription(pc2.localDescription);
|
||||||
|
ok(pc1.currentRemoteDescription, "pc1 currentRemoteDescription is set");
|
||||||
|
is(pc1.pendingRemoteDescription, null, "pc1 pendingRemoteDescription is null");
|
||||||
|
ok(pc1.remoteDescription, "pc1 remoteDescription is set");
|
||||||
|
isSimilarDescription(pc1.currentRemoteDescription, pc1.currentRemoteDescription, "currentRemoteDescription");
|
||||||
|
isSimilarDescription(pc1.remoteDescription, pc1.remoteDescription, "remoteDescription");
|
||||||
|
isSimilarDescription(pc1.remoteDescription, pc1.currentRemoteDescription, "remote and current");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
async function checkDuplicateFingerprintQuirk() {
|
||||||
|
await withPrefs([["media.peerconnection.sdp.quirk.duplicate_fingerprint.allowlist", "example.com"]],
|
||||||
|
async () => {
|
||||||
|
const pc1 = new RTCPeerConnection();
|
||||||
|
const pc2 = new RTCPeerConnection();
|
||||||
|
|
||||||
|
pc1.onicecandidate = e => addIce(pc2, e.candidate, generateErrorCallback());
|
||||||
|
pc2.onicecandidate = e => addIce(pc1, e.candidate, generateErrorCallback());
|
||||||
|
|
||||||
|
const stream = await navigator.mediaDevices.getUserMedia({
|
||||||
|
video: true, audio: true
|
||||||
|
});
|
||||||
|
v1.srcObject = stream;
|
||||||
|
for (const track of stream.getTracks()) {
|
||||||
|
pc1.addTrack(track, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fingerprints = ({sdp}) => sdp.match(/a=fingerprint/g).length;
|
||||||
|
|
||||||
|
// with implicit offer and answer
|
||||||
|
await pc1.setLocalDescription();
|
||||||
|
is(pc1.pendingLocalDescription, pc1.localDescription);
|
||||||
|
is(fingerprints(pc1.localDescription), 3, "implicit pc1 fingerprints");
|
||||||
|
await pc2.setRemoteDescription(pc1.localDescription);
|
||||||
|
await pc2.setLocalDescription();
|
||||||
|
is(pc2.currentLocalDescription, pc2.localDescription);
|
||||||
|
is(fingerprints(pc2.localDescription), 3, "implicit pc2 fingerprints");
|
||||||
|
await pc1.setRemoteDescription(pc2.localDescription);
|
||||||
|
|
||||||
|
// with explicit offer and answer
|
||||||
|
const offer = await pc1.createOffer();
|
||||||
|
is(fingerprints(offer), 3, "offer fingerprints");
|
||||||
|
await pc1.setLocalDescription(offer);
|
||||||
|
is(pc1.pendingLocalDescription, pc1.localDescription);
|
||||||
|
is(fingerprints(pc1.localDescription), 3, "explicit pc1 fingerprints");
|
||||||
|
await pc2.setRemoteDescription(pc1.localDescription);
|
||||||
|
const answer = pc2.createAnswer();
|
||||||
|
is(fingerprints(offer), 3, "answer fingerprints");
|
||||||
|
await pc2.setLocalDescription(answer);
|
||||||
|
is(pc2.currentLocalDescription, pc2.localDescription);
|
||||||
|
is(fingerprints(pc2.localDescription), 3, "explicit pc2 fingerprints");
|
||||||
|
await pc1.setRemoteDescription(pc2.localDescription);
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
await pc1.setLocalDescription();
|
];
|
||||||
is(pc1.currentLocalDescription, null, "pc1 currentLocalDescription is null");
|
|
||||||
ok(pc1.pendingLocalDescription, "pc1 pendingLocalDescription is set");
|
|
||||||
ok(pc1.localDescription, "pc1 localDescription is set");
|
|
||||||
isSimilarDescription(pc1.pendingLocalDescription, pc1.pendingLocalDescription, "pendingLocalDescription");
|
|
||||||
isSimilarDescription(pc1.localDescription, pc1.localDescription, "localDescription");
|
|
||||||
isSimilarDescription(pc1.localDescription, pc1.pendingLocalDescription, "local and pending");
|
|
||||||
|
|
||||||
await pc2.setRemoteDescription(pc1.localDescription);
|
runNetworkTest(async () => {
|
||||||
is(pc2.currentRemoteDescription, null, "pc2 currentRemoteDescription is null");
|
for (const test of tests) {
|
||||||
ok(pc2.pendingRemoteDescription, "pc2 pendingRemoteDescription is set");
|
info(`Running test: ${test.name}`);
|
||||||
ok(pc2.remoteDescription, "pc2 remoteDescription is set");
|
await test();
|
||||||
isSimilarDescription(pc2.pendingRemoteDescription, pc2.pendingRemoteDescription, "pendingRemoteDescription");
|
info(`Done running test: ${test.name}`);
|
||||||
isSimilarDescription(pc2.remoteDescription, pc2.remoteDescription, "remoteDescription");
|
}
|
||||||
isSimilarDescription(pc2.remoteDescription, pc2.pendingRemoteDescription, "remote and pending");
|
|
||||||
|
|
||||||
await pc2.setLocalDescription();
|
|
||||||
ok(pc2.currentLocalDescription, "pc2 currentLocalDescription is set");
|
|
||||||
is(pc2.pendingLocalDescription, null, "pc2 pendingLocalDescription is null");
|
|
||||||
ok(pc2.localDescription, "pc2 localDescription is set");
|
|
||||||
isSimilarDescription(pc2.currentLocalDescription, pc2.currentLocalDescription, "currentLocalDescription");
|
|
||||||
isSimilarDescription(pc2.localDescription, pc2.localDescription, "localDescription");
|
|
||||||
isSimilarDescription(pc2.localDescription, pc2.currentLocalDescription, "local and current");
|
|
||||||
|
|
||||||
await pc1.setRemoteDescription(pc2.localDescription);
|
|
||||||
ok(pc1.currentRemoteDescription, "pc1 currentRemoteDescription is set");
|
|
||||||
is(pc1.pendingRemoteDescription, null, "pc1 pendingRemoteDescription is null");
|
|
||||||
ok(pc1.remoteDescription, "pc1 remoteDescription is set");
|
|
||||||
isSimilarDescription(pc1.currentRemoteDescription, pc1.currentRemoteDescription, "currentRemoteDescription");
|
|
||||||
isSimilarDescription(pc1.remoteDescription, pc1.remoteDescription, "remoteDescription");
|
|
||||||
isSimilarDescription(pc1.remoteDescription, pc1.currentRemoteDescription, "remote and current");
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
|
@ -117,6 +117,8 @@ interface PeerConnectionImpl {
|
||||||
attribute DOMString peerIdentity;
|
attribute DOMString peerIdentity;
|
||||||
readonly attribute boolean privacyRequested;
|
readonly attribute boolean privacyRequested;
|
||||||
|
|
||||||
|
readonly attribute boolean duplicateFingerprintQuirk;
|
||||||
|
|
||||||
readonly attribute RTCSctpTransport? sctp;
|
readonly attribute RTCSctpTransport? sctp;
|
||||||
|
|
||||||
/* Data channels */
|
/* Data channels */
|
||||||
|
|
|
@ -240,6 +240,7 @@ pref("media.videocontrols.keyboard-tab-to-all-controls", true);
|
||||||
pref("media.navigator.video.use_transport_cc", true);
|
pref("media.navigator.video.use_transport_cc", true);
|
||||||
pref("media.peerconnection.video.use_rtx", true);
|
pref("media.peerconnection.video.use_rtx", true);
|
||||||
pref("media.peerconnection.video.use_rtx.blocklist", "doxy.me,*.doxy.me");
|
pref("media.peerconnection.video.use_rtx.blocklist", "doxy.me,*.doxy.me");
|
||||||
|
pref("media.peerconnection.sdp.quirk.duplicate_fingerprint.allowlist", "");
|
||||||
pref("media.navigator.video.use_tmmbr", false);
|
pref("media.navigator.video.use_tmmbr", false);
|
||||||
pref("media.navigator.audio.use_fec", true);
|
pref("media.navigator.audio.use_fec", true);
|
||||||
pref("media.navigator.video.offer_rtcp_rsize", true);
|
pref("media.navigator.video.offer_rtcp_rsize", true);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче