firefox_shim.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. /*
  2. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  3. *
  4. * Use of this source code is governed by a BSD-style license
  5. * that can be found in the LICENSE file in the root of the source
  6. * tree.
  7. */
  8. /* eslint-env node */
  9. 'use strict';
  10. Object.defineProperty(exports, "__esModule", {
  11. value: true
  12. });
  13. exports.shimAddTransceiver = shimAddTransceiver;
  14. exports.shimCreateAnswer = shimCreateAnswer;
  15. exports.shimCreateOffer = shimCreateOffer;
  16. Object.defineProperty(exports, "shimGetDisplayMedia", {
  17. enumerable: true,
  18. get: function get() {
  19. return _getdisplaymedia.shimGetDisplayMedia;
  20. }
  21. });
  22. exports.shimGetParameters = shimGetParameters;
  23. Object.defineProperty(exports, "shimGetUserMedia", {
  24. enumerable: true,
  25. get: function get() {
  26. return _getusermedia.shimGetUserMedia;
  27. }
  28. });
  29. exports.shimOnTrack = shimOnTrack;
  30. exports.shimPeerConnection = shimPeerConnection;
  31. exports.shimRTCDataChannel = shimRTCDataChannel;
  32. exports.shimReceiverGetStats = shimReceiverGetStats;
  33. exports.shimRemoveStream = shimRemoveStream;
  34. exports.shimSenderGetStats = shimSenderGetStats;
  35. var utils = _interopRequireWildcard(require("../utils"));
  36. var _getusermedia = require("./getusermedia");
  37. var _getdisplaymedia = require("./getdisplaymedia");
  38. function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
  39. function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
  40. function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
  41. function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
  42. function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
  43. function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
  44. function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
  45. function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
  46. function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
  47. function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
  48. function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
  49. function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
  50. function shimOnTrack(window) {
  51. if (_typeof(window) === 'object' && window.RTCTrackEvent && 'receiver' in window.RTCTrackEvent.prototype && !('transceiver' in window.RTCTrackEvent.prototype)) {
  52. Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
  53. get: function get() {
  54. return {
  55. receiver: this.receiver
  56. };
  57. }
  58. });
  59. }
  60. }
  61. function shimPeerConnection(window, browserDetails) {
  62. if (_typeof(window) !== 'object' || !(window.RTCPeerConnection || window.mozRTCPeerConnection)) {
  63. return; // probably media.peerconnection.enabled=false in about:config
  64. }
  65. if (!window.RTCPeerConnection && window.mozRTCPeerConnection) {
  66. // very basic support for old versions.
  67. window.RTCPeerConnection = window.mozRTCPeerConnection;
  68. }
  69. if (browserDetails.version < 53) {
  70. // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
  71. ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) {
  72. var nativeMethod = window.RTCPeerConnection.prototype[method];
  73. var methodObj = _defineProperty({}, method, function () {
  74. arguments[0] = new (method === 'addIceCandidate' ? window.RTCIceCandidate : window.RTCSessionDescription)(arguments[0]);
  75. return nativeMethod.apply(this, arguments);
  76. });
  77. window.RTCPeerConnection.prototype[method] = methodObj[method];
  78. });
  79. }
  80. var modernStatsTypes = {
  81. inboundrtp: 'inbound-rtp',
  82. outboundrtp: 'outbound-rtp',
  83. candidatepair: 'candidate-pair',
  84. localcandidate: 'local-candidate',
  85. remotecandidate: 'remote-candidate'
  86. };
  87. var nativeGetStats = window.RTCPeerConnection.prototype.getStats;
  88. window.RTCPeerConnection.prototype.getStats = function getStats() {
  89. var _arguments = Array.prototype.slice.call(arguments),
  90. selector = _arguments[0],
  91. onSucc = _arguments[1],
  92. onErr = _arguments[2];
  93. return nativeGetStats.apply(this, [selector || null]).then(function (stats) {
  94. if (browserDetails.version < 53 && !onSucc) {
  95. // Shim only promise getStats with spec-hyphens in type names
  96. // Leave callback version alone; misc old uses of forEach before Map
  97. try {
  98. stats.forEach(function (stat) {
  99. stat.type = modernStatsTypes[stat.type] || stat.type;
  100. });
  101. } catch (e) {
  102. if (e.name !== 'TypeError') {
  103. throw e;
  104. }
  105. // Avoid TypeError: "type" is read-only, in old versions. 34-43ish
  106. stats.forEach(function (stat, i) {
  107. stats.set(i, Object.assign({}, stat, {
  108. type: modernStatsTypes[stat.type] || stat.type
  109. }));
  110. });
  111. }
  112. }
  113. return stats;
  114. }).then(onSucc, onErr);
  115. };
  116. }
  117. function shimSenderGetStats(window) {
  118. if (!(_typeof(window) === 'object' && window.RTCPeerConnection && window.RTCRtpSender)) {
  119. return;
  120. }
  121. if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) {
  122. return;
  123. }
  124. var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
  125. if (origGetSenders) {
  126. window.RTCPeerConnection.prototype.getSenders = function getSenders() {
  127. var _this = this;
  128. var senders = origGetSenders.apply(this, []);
  129. senders.forEach(function (sender) {
  130. return sender._pc = _this;
  131. });
  132. return senders;
  133. };
  134. }
  135. var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
  136. if (origAddTrack) {
  137. window.RTCPeerConnection.prototype.addTrack = function addTrack() {
  138. var sender = origAddTrack.apply(this, arguments);
  139. sender._pc = this;
  140. return sender;
  141. };
  142. }
  143. window.RTCRtpSender.prototype.getStats = function getStats() {
  144. return this.track ? this._pc.getStats(this.track) : Promise.resolve(new Map());
  145. };
  146. }
  147. function shimReceiverGetStats(window) {
  148. if (!(_typeof(window) === 'object' && window.RTCPeerConnection && window.RTCRtpSender)) {
  149. return;
  150. }
  151. if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) {
  152. return;
  153. }
  154. var origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;
  155. if (origGetReceivers) {
  156. window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {
  157. var _this2 = this;
  158. var receivers = origGetReceivers.apply(this, []);
  159. receivers.forEach(function (receiver) {
  160. return receiver._pc = _this2;
  161. });
  162. return receivers;
  163. };
  164. }
  165. utils.wrapPeerConnectionEvent(window, 'track', function (e) {
  166. e.receiver._pc = e.srcElement;
  167. return e;
  168. });
  169. window.RTCRtpReceiver.prototype.getStats = function getStats() {
  170. return this._pc.getStats(this.track);
  171. };
  172. }
  173. function shimRemoveStream(window) {
  174. if (!window.RTCPeerConnection || 'removeStream' in window.RTCPeerConnection.prototype) {
  175. return;
  176. }
  177. window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
  178. var _this3 = this;
  179. utils.deprecated('removeStream', 'removeTrack');
  180. this.getSenders().forEach(function (sender) {
  181. if (sender.track && stream.getTracks().includes(sender.track)) {
  182. _this3.removeTrack(sender);
  183. }
  184. });
  185. };
  186. }
  187. function shimRTCDataChannel(window) {
  188. // rename DataChannel to RTCDataChannel (native fix in FF60):
  189. // https://bugzilla.mozilla.org/show_bug.cgi?id=1173851
  190. if (window.DataChannel && !window.RTCDataChannel) {
  191. window.RTCDataChannel = window.DataChannel;
  192. }
  193. }
  194. function shimAddTransceiver(window) {
  195. // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
  196. // Firefox ignores the init sendEncodings options passed to addTransceiver
  197. // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
  198. if (!(_typeof(window) === 'object' && window.RTCPeerConnection)) {
  199. return;
  200. }
  201. var origAddTransceiver = window.RTCPeerConnection.prototype.addTransceiver;
  202. if (origAddTransceiver) {
  203. window.RTCPeerConnection.prototype.addTransceiver = function addTransceiver() {
  204. this.setParametersPromises = [];
  205. // WebIDL input coercion and validation
  206. var sendEncodings = arguments[1] && arguments[1].sendEncodings;
  207. if (sendEncodings === undefined) {
  208. sendEncodings = [];
  209. }
  210. sendEncodings = _toConsumableArray(sendEncodings);
  211. var shouldPerformCheck = sendEncodings.length > 0;
  212. if (shouldPerformCheck) {
  213. // If sendEncodings params are provided, validate grammar
  214. sendEncodings.forEach(function (encodingParam) {
  215. if ('rid' in encodingParam) {
  216. var ridRegex = /^[a-z0-9]{0,16}$/i;
  217. if (!ridRegex.test(encodingParam.rid)) {
  218. throw new TypeError('Invalid RID value provided.');
  219. }
  220. }
  221. if ('scaleResolutionDownBy' in encodingParam) {
  222. if (!(parseFloat(encodingParam.scaleResolutionDownBy) >= 1.0)) {
  223. throw new RangeError('scale_resolution_down_by must be >= 1.0');
  224. }
  225. }
  226. if ('maxFramerate' in encodingParam) {
  227. if (!(parseFloat(encodingParam.maxFramerate) >= 0)) {
  228. throw new RangeError('max_framerate must be >= 0.0');
  229. }
  230. }
  231. });
  232. }
  233. var transceiver = origAddTransceiver.apply(this, arguments);
  234. if (shouldPerformCheck) {
  235. // Check if the init options were applied. If not we do this in an
  236. // asynchronous way and save the promise reference in a global object.
  237. // This is an ugly hack, but at the same time is way more robust than
  238. // checking the sender parameters before and after the createOffer
  239. // Also note that after the createoffer we are not 100% sure that
  240. // the params were asynchronously applied so we might miss the
  241. // opportunity to recreate offer.
  242. var sender = transceiver.sender;
  243. var params = sender.getParameters();
  244. if (!('encodings' in params) ||
  245. // Avoid being fooled by patched getParameters() below.
  246. params.encodings.length === 1 && Object.keys(params.encodings[0]).length === 0) {
  247. params.encodings = sendEncodings;
  248. sender.sendEncodings = sendEncodings;
  249. this.setParametersPromises.push(sender.setParameters(params).then(function () {
  250. delete sender.sendEncodings;
  251. })["catch"](function () {
  252. delete sender.sendEncodings;
  253. }));
  254. }
  255. }
  256. return transceiver;
  257. };
  258. }
  259. }
  260. function shimGetParameters(window) {
  261. if (!(_typeof(window) === 'object' && window.RTCRtpSender)) {
  262. return;
  263. }
  264. var origGetParameters = window.RTCRtpSender.prototype.getParameters;
  265. if (origGetParameters) {
  266. window.RTCRtpSender.prototype.getParameters = function getParameters() {
  267. var params = origGetParameters.apply(this, arguments);
  268. if (!('encodings' in params)) {
  269. params.encodings = [].concat(this.sendEncodings || [{}]);
  270. }
  271. return params;
  272. };
  273. }
  274. }
  275. function shimCreateOffer(window) {
  276. // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
  277. // Firefox ignores the init sendEncodings options passed to addTransceiver
  278. // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
  279. if (!(_typeof(window) === 'object' && window.RTCPeerConnection)) {
  280. return;
  281. }
  282. var origCreateOffer = window.RTCPeerConnection.prototype.createOffer;
  283. window.RTCPeerConnection.prototype.createOffer = function createOffer() {
  284. var _arguments2 = arguments,
  285. _this4 = this;
  286. if (this.setParametersPromises && this.setParametersPromises.length) {
  287. return Promise.all(this.setParametersPromises).then(function () {
  288. return origCreateOffer.apply(_this4, _arguments2);
  289. })["finally"](function () {
  290. _this4.setParametersPromises = [];
  291. });
  292. }
  293. return origCreateOffer.apply(this, arguments);
  294. };
  295. }
  296. function shimCreateAnswer(window) {
  297. // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
  298. // Firefox ignores the init sendEncodings options passed to addTransceiver
  299. // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
  300. if (!(_typeof(window) === 'object' && window.RTCPeerConnection)) {
  301. return;
  302. }
  303. var origCreateAnswer = window.RTCPeerConnection.prototype.createAnswer;
  304. window.RTCPeerConnection.prototype.createAnswer = function createAnswer() {
  305. var _arguments3 = arguments,
  306. _this5 = this;
  307. if (this.setParametersPromises && this.setParametersPromises.length) {
  308. return Promise.all(this.setParametersPromises).then(function () {
  309. return origCreateAnswer.apply(_this5, _arguments3);
  310. })["finally"](function () {
  311. _this5.setParametersPromises = [];
  312. });
  313. }
  314. return origCreateAnswer.apply(this, arguments);
  315. };
  316. }