Skip to content

Commit

Permalink
Merge pull request #549 from webrtc/unRevert
Browse files Browse the repository at this point in the history
Revert "Revert "Merge pull request #527 from fippo/rtcicecandidate""
  • Loading branch information
KaptenJansson authored May 17, 2017
2 parents 81f3c7a + 13d5f20 commit b4c5526
Show file tree
Hide file tree
Showing 6 changed files with 278 additions and 2 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"test": "grunt && mocha test/unit && node test/run-tests.js"
},
"dependencies": {
"sdp": "^1.5.0"
"sdp": "^1.5.1"
},
"engines": {
"npm": ">=3.10.0",
Expand Down
5 changes: 5 additions & 0 deletions src/js/adapter_core.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
var edgeShim = require('./edge/edge_shim') || null;
var firefoxShim = require('./firefox/firefox_shim') || null;
var safariShim = require('./safari/safari_shim') || null;
var commonShim = require('./common');

// Shim browser if found.
switch (browserDetails.browser) {
Expand Down Expand Up @@ -96,5 +97,9 @@
break;
default:
logging('Unsupported browser!');
break;
}

// common shims.
commonShim.shimRTCIceCandidate(); // augmented RTCIceCandidate
})();
108 changes: 108 additions & 0 deletions src/js/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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';

var SDPUtils = require('sdp');

// Wraps the peerconnection event eventNameToWrap in a function
// which returns the modified event object.
function wrapPeerConnectionEvent(eventNameToWrap, wrapper) {
if (!window.RTCPeerConnection) {
return;
}
var proto = window.RTCPeerConnection.prototype;
var nativeAddEventListener = proto.addEventListener;
proto.addEventListener = function(nativeEventName, cb) {
if (nativeEventName !== eventNameToWrap) {
return nativeAddEventListener.apply(this, arguments);
}
var wrappedCallback = function(e) {
cb(wrapper(e));
};
this._eventMap = this._eventMap || {};
this._eventMap[cb] = wrappedCallback;
return nativeAddEventListener.apply(this, [nativeEventName,
wrappedCallback]);
};

var nativeRemoveEventListener = proto.removeEventListener;
proto.removeEventListener = function(nativeEventName, cb) {
if (nativeEventName !== eventNameToWrap || !this._eventMap
|| !this._eventMap[cb]) {
return nativeRemoveEventListener.apply(this, arguments);
}
var unwrappedCb = this._eventMap[cb];
delete this._eventMap[cb];
return nativeRemoveEventListener.apply(this, [nativeEventName,
unwrappedCb]);
};

Object.defineProperty(proto, 'on' + eventNameToWrap, {
get: function() {
return this['_on' + eventNameToWrap];
},
set: function(cb) {
if (this['_on' + eventNameToWrap]) {
this.removeEventListener(eventNameToWrap,
this['_on' + eventNameToWrap]);
}
this.addEventListener(eventNameToWrap,
this['_on' + eventNameToWrap] = cb);
}
});
}

module.exports = {
shimRTCIceCandidate: function() {
// foundation is arbitrarily chosen as an indicator for full support for
// https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface
if (window.RTCIceCandidate && 'foundation' in
window.RTCIceCandidate.prototype) {
return;
}

var NativeRTCIceCandidate = window.RTCIceCandidate;
window.RTCIceCandidate = function(args) {
// Remove the a= which shouldn't be part of the candidate string.
if (typeof args === 'object' && args.candidate &&
args.candidate.indexOf('a=') === 0) {
args = JSON.parse(JSON.stringify(args));
args.candidate = args.candidate.substr(2);
}

// Augment the native candidate with the parsed fields.
var nativeCandidate = new NativeRTCIceCandidate(args);
var parsedCandidate = SDPUtils.parseCandidate(args.candidate);
var augmentedCandidate = Object.assign(nativeCandidate,
parsedCandidate);

// Add a serializer that does not serialize the extra attributes.
augmentedCandidate.toJSON = function() {
return {
candidate: augmentedCandidate.candidate,
sdpMid: augmentedCandidate.sdpMid,
sdpMLineIndex: augmentedCandidate.sdpMLineIndex,
ufrag: augmentedCandidate.ufrag
};
};
return augmentedCandidate;
};

// Hook up the augmented candidate in onicecandidate and
// addEventListener('icecandidate', ...)
wrapPeerConnectionEvent('icecandidate', function(e) {
if (e.candidate) {
Object.defineProperty(e, 'candidate', {
value: new RTCIceCandidate(e.candidate), writable: 'false'
});
}
return e;
});
}
};
2 changes: 1 addition & 1 deletion src/js/edge/edge_shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module.exports = {
shimPeerConnection: function() {
if (window.RTCIceGatherer) {
// ORTC defines an RTCIceCandidate object but no constructor.
// Not implemented in Edge.
// Not implemented in in some versions of Edge.
if (!window.RTCIceCandidate) {
window.RTCIceCandidate = function(args) {
return args;
Expand Down
83 changes: 83 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2963,6 +2963,89 @@ test('ontrack', function(t) {
});
});

test('augmented RTCIceCandidate', function(t) {
var driver = seleniumHelpers.buildDriver();

// Define test.
var testDefinition = function() {
var callback = arguments[arguments.length - 1];

var hasProperty = false;
var pc = new RTCPeerConnection();
pc.addEventListener('icecandidate', function(e) {
if (!e.candidate) {
callback(hasProperty);
return;
}
hasProperty = e.candidate.port !== undefined;
});
pc.createOffer({offerToReceiveAudio: true})
.then(function(offer) {
return pc.setLocalDescription(offer);
});
};

// Run test.
seleniumHelpers.loadTestPage(driver)
.then(function() {
t.pass('Page loaded');
return driver.executeAsyncScript(testDefinition);
})
.then(function(hasProperty) {
t.ok(hasProperty === true, 'candidate was augmented');
t.end();
})
.then(null, function(err) {
if (err !== 'skip-test') {
t.fail(err);
}
t.end();
});
});

test('augmented RTCIceCandidate eventlistener overload', function(t) {
var driver = seleniumHelpers.buildDriver();

// Define test.
var testDefinition = function() {
var callback = arguments[arguments.length - 1];

var pc = new RTCPeerConnection();
var calledWrong = false;
var wrongCb = function(e) {
calledWrong = true;
};
pc.addEventListener('icecandidate', wrongCb);
pc.removeEventListener('icecandidate', wrongCb);
pc.addEventListener('icecandidate', function(e) {
if (!e.candidate) {
callback(calledWrong);
}
});
pc.createOffer({offerToReceiveAudio: true})
.then(function(offer) {
return pc.setLocalDescription(offer);
});
};

// Run test.
seleniumHelpers.loadTestPage(driver)
.then(function() {
t.pass('Page loaded');
return driver.executeAsyncScript(testDefinition);
})
.then(function(calledWrongCallback) {
t.ok(calledWrongCallback === false, 'the removed callback was not called.');
t.end();
})
.then(null, function(err) {
if (err !== 'skip-test') {
t.fail(err);
}
t.end();
});
});

// This MUST to be the last test since it loads adapter
// again which may result in unintended behaviour.
test('Non-module logging to console still works', function(t) {
Expand Down
80 changes: 80 additions & 0 deletions test/unit/rtcicecandidate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* 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 */
const chai = require('chai');
const expect = chai.expect;

describe('RTCIceCandidate', () => {
const shim = require('../../src/js/common');
beforeEach(() => {
global.window = global;
window.RTCIceCandidate = function(args) {
return args;
};
window.RTCPeerConnection = function() {};
shim.shimRTCIceCandidate();
});

const candidateString = 'candidate:702786350 2 udp 41819902 8.8.8.8 60769 ' +
'typ relay raddr 8.8.8.8 rport 1234 ' +
'tcptype active ' +
'ufrag abc';

describe('constructor', () => {
it('retains the candidate', () => {
const candidate = new RTCIceCandidate({
candidate: candidateString,
sdpMid: 'audio',
sdpMLineIndex: 0
});
expect(candidate.candidate).to.equal(candidateString);
expect(candidate.sdpMid).to.equal('audio');
expect(candidate.sdpMLineIndex).to.equal(0);
});

it('drops the a= part of the candidate if present', () => {
const candidate = new RTCIceCandidate({
candidate: 'a=' + candidateString,
sdpMid: 'audio',
sdpMLineIndex: 0
});
expect(candidate.candidate).to.equal(candidateString);
});

it('parses the candidate', () => {
const candidate = new RTCIceCandidate({
candidate: candidateString,
sdpMid: 'audio',
sdpMLineIndex: 0
});
expect(candidate.foundation).to.equal('702786350');
// expect(candidate.component).to.equal('2'); // TODO
expect(candidate.priority).to.equal(41819902);
expect(candidate.ip).to.equal('8.8.8.8');
expect(candidate.protocol).to.equal('udp');
expect(candidate.port).to.equal(60769);
expect(candidate.type).to.equal('relay');
expect(candidate.tcpType).to.equal('active');
expect(candidate.relatedAddress).to.equal('8.8.8.8');
expect(candidate.relatedPort).to.equal(1234);
expect(candidate.ufrag).to.equal('abc');
});
});

it('does not serialize the extra attributes', () => {
const candidate = new RTCIceCandidate({
candidate: candidateString,
sdpMid: 'audio',
sdpMLineIndex: 0,
ufrag: 'someufrag'
});
const serialized = JSON.stringify(candidate);
// there should be only 4 items in the JSON.
expect(Object.keys(JSON.parse(serialized)).length).to.equal(4);
});
});

0 comments on commit b4c5526

Please sign in to comment.