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.

RTCBrowserType.js 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. import { getLogger } from 'jitsi-meet-logger';
  2. let browserVersion; // eslint-disable-line prefer-const
  3. let currentBrowser;
  4. const logger = getLogger(__filename);
  5. const RTCBrowserType = {
  6. RTC_BROWSER_CHROME: 'rtc_browser.chrome',
  7. RTC_BROWSER_OPERA: 'rtc_browser.opera',
  8. RTC_BROWSER_FIREFOX: 'rtc_browser.firefox',
  9. RTC_BROWSER_IEXPLORER: 'rtc_browser.iexplorer',
  10. RTC_BROWSER_SAFARI: 'rtc_browser.safari',
  11. RTC_BROWSER_NWJS: 'rtc_browser.nwjs',
  12. RTC_BROWSER_ELECTRON: 'rtc_browser.electron',
  13. RTC_BROWSER_REACT_NATIVE: 'rtc_browser.react-native',
  14. /**
  15. * Tells whether or not the <tt>MediaStream/tt> is removed from
  16. * the <tt>PeerConnection</tt> and disposed on video mute (in order to turn
  17. * off the camera device).
  18. * @return {boolean} <tt>true</tt> if the current browser supports this
  19. * strategy or <tt>false</tt> otherwise.
  20. */
  21. doesVideoMuteByStreamRemove() {
  22. return !RTCBrowserType.isFirefox();
  23. },
  24. /**
  25. * Gets current browser type.
  26. * @returns {string}
  27. */
  28. getBrowserType() {
  29. return currentBrowser;
  30. },
  31. /**
  32. * Gets current browser name, split from the type.
  33. * @returns {string}
  34. */
  35. getBrowserName() {
  36. const isAndroid = navigator.userAgent.indexOf('Android') !== -1;
  37. if (isAndroid) {
  38. return 'android';
  39. }
  40. return currentBrowser.split('rtc_browser.')[1];
  41. },
  42. /**
  43. * Checks if current browser is Chrome.
  44. * @returns {boolean}
  45. */
  46. isChrome() {
  47. return currentBrowser === RTCBrowserType.RTC_BROWSER_CHROME;
  48. },
  49. /**
  50. * Checks if current browser is Opera.
  51. * @returns {boolean}
  52. */
  53. isOpera() {
  54. return currentBrowser === RTCBrowserType.RTC_BROWSER_OPERA;
  55. },
  56. /**
  57. * Checks if current browser is Firefox.
  58. * @returns {boolean}
  59. */
  60. isFirefox() {
  61. return currentBrowser === RTCBrowserType.RTC_BROWSER_FIREFOX;
  62. },
  63. /**
  64. * Checks if current browser is Internet Explorer.
  65. * @returns {boolean}
  66. */
  67. isIExplorer() {
  68. return currentBrowser === RTCBrowserType.RTC_BROWSER_IEXPLORER;
  69. },
  70. /**
  71. * Checks if current browser is Safari.
  72. * @returns {boolean}
  73. */
  74. isSafari() {
  75. return currentBrowser === RTCBrowserType.RTC_BROWSER_SAFARI;
  76. },
  77. /**
  78. * Checks if current environment is NWJS.
  79. * @returns {boolean}
  80. */
  81. isNWJS() {
  82. return currentBrowser === RTCBrowserType.RTC_BROWSER_NWJS;
  83. },
  84. /**
  85. * Checks if current environment is Electron.
  86. * @returns {boolean}
  87. */
  88. isElectron() {
  89. return currentBrowser === RTCBrowserType.RTC_BROWSER_ELECTRON;
  90. },
  91. /**
  92. * Check whether or not the current browser support peer to peer connections
  93. * @return {boolean} <tt>true</tt> if p2p is supported or <tt>false</tt>
  94. * otherwise.
  95. */
  96. isP2PSupported() {
  97. return !RTCBrowserType.isFirefox() && !RTCBrowserType.isReactNative();
  98. },
  99. /**
  100. * Checks if current environment is React Native.
  101. * @returns {boolean}
  102. */
  103. isReactNative() {
  104. return currentBrowser === RTCBrowserType.RTC_BROWSER_REACT_NATIVE;
  105. },
  106. /**
  107. * Checks if Temasys RTC plugin is used.
  108. * @returns {boolean}
  109. */
  110. isTemasysPluginUsed() {
  111. // Temasys do not support Microsoft Edge:
  112. // http://support.temasys.com.sg/support/solutions/articles/
  113. // 5000654345-can-the-temasys-webrtc-plugin-be-used-with-microsoft-edge-
  114. return (
  115. RTCBrowserType.isSafari()
  116. || (RTCBrowserType.isIExplorer()
  117. && RTCBrowserType.getIExplorerVersion() < 12)
  118. );
  119. },
  120. /**
  121. * Checks if the current browser triggers 'onmute'/'onunmute' events when
  122. * user's connection is interrupted and the video stops playback.
  123. * @returns {*|boolean} 'true' if the event is supported or 'false'
  124. * otherwise.
  125. */
  126. isVideoMuteOnConnInterruptedSupported() {
  127. return RTCBrowserType.isChrome();
  128. },
  129. /**
  130. * Returns Firefox version.
  131. * @returns {number|null}
  132. */
  133. getFirefoxVersion() {
  134. return RTCBrowserType.isFirefox() ? browserVersion : null;
  135. },
  136. /**
  137. * Returns Chrome version.
  138. * @returns {number|null}
  139. */
  140. getChromeVersion() {
  141. return RTCBrowserType.isChrome() ? browserVersion : null;
  142. },
  143. /**
  144. * Returns Internet Explorer version.
  145. *
  146. * @returns {number|null}
  147. */
  148. getIExplorerVersion() {
  149. return RTCBrowserType.isIExplorer() ? browserVersion : null;
  150. },
  151. usesPlanB() {
  152. return !RTCBrowserType.usesUnifiedPlan();
  153. },
  154. usesUnifiedPlan() {
  155. return RTCBrowserType.isFirefox();
  156. },
  157. /**
  158. * Whether jitsi-meet supports simulcast on the current browser.
  159. * @returns {boolean}
  160. */
  161. supportsSimulcast() {
  162. // This mirrors what sdp-simulcast uses (which is used when deciding
  163. // whether to actually enable simulcast or not).
  164. // TODO: the logic should be in one single place.
  165. return window.chrome !== undefined;
  166. },
  167. supportsRtx() {
  168. return !RTCBrowserType.isFirefox();
  169. }
  170. // Add version getters for other browsers when needed
  171. };
  172. /**
  173. * detectOpera() must be called before detectChrome() !!!
  174. * otherwise Opera wil be detected as Chrome
  175. */
  176. function detectChrome() {
  177. if (navigator.webkitGetUserMedia) {
  178. currentBrowser = RTCBrowserType.RTC_BROWSER_CHROME;
  179. const userAgent = navigator.userAgent.toLowerCase();
  180. // We can assume that user agent is chrome, because it's
  181. // enforced when 'ext' streaming method is set
  182. const ver = parseInt(userAgent.match(/chrome\/(\d+)\./)[1], 10);
  183. logger.log(`This appears to be Chrome, ver: ${ver}`);
  184. return ver;
  185. }
  186. return null;
  187. }
  188. /**
  189. *
  190. */
  191. function detectOpera() {
  192. const userAgent = navigator.userAgent;
  193. if (userAgent.match(/Opera|OPR/)) {
  194. currentBrowser = RTCBrowserType.RTC_BROWSER_OPERA;
  195. const version = userAgent.match(/(Opera|OPR) ?\/?(\d+)\.?/)[2];
  196. logger.info(`This appears to be Opera, ver: ${version}`);
  197. return version;
  198. }
  199. return null;
  200. }
  201. /**
  202. *
  203. */
  204. function detectFirefox() {
  205. if (navigator.mozGetUserMedia) {
  206. currentBrowser = RTCBrowserType.RTC_BROWSER_FIREFOX;
  207. const version = parseInt(
  208. navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
  209. logger.log(`This appears to be Firefox, ver: ${version}`);
  210. return version;
  211. }
  212. return null;
  213. }
  214. /**
  215. *
  216. */
  217. function detectSafari() {
  218. if (/^((?!chrome).)*safari/i.test(navigator.userAgent)) {
  219. currentBrowser = RTCBrowserType.RTC_BROWSER_SAFARI;
  220. logger.info('This appears to be Safari');
  221. // FIXME detect Safari version when needed
  222. return 1;
  223. }
  224. return null;
  225. }
  226. /**
  227. *
  228. */
  229. function detectIE() {
  230. let version;
  231. const ua = window.navigator.userAgent;
  232. const msie = ua.indexOf('MSIE ');
  233. if (msie > 0) {
  234. // IE 10 or older => return version number
  235. version = parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
  236. }
  237. const trident = ua.indexOf('Trident/');
  238. if (!version && trident > 0) {
  239. // IE 11 => return version number
  240. const rv = ua.indexOf('rv:');
  241. version = parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
  242. }
  243. const edge = ua.indexOf('Edge/');
  244. if (!version && edge > 0) {
  245. // IE 12 => return version number
  246. version = parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
  247. }
  248. if (version) {
  249. currentBrowser = RTCBrowserType.RTC_BROWSER_IEXPLORER;
  250. logger.info(`This appears to be IExplorer, ver: ${version}`);
  251. }
  252. return version;
  253. }
  254. /**
  255. * Detects Electron environment.
  256. */
  257. function detectElectron() {
  258. const userAgent = navigator.userAgent;
  259. if (userAgent.match(/Electron/)) {
  260. currentBrowser = RTCBrowserType.RTC_BROWSER_ELECTRON;
  261. const version = userAgent.match(/Electron\/([\d.]+)/)[1];
  262. logger.info(`This appears to be Electron, ver: ${version}`);
  263. return version;
  264. }
  265. return null;
  266. }
  267. /**
  268. *
  269. */
  270. function detectNWJS() {
  271. const userAgent = navigator.userAgent;
  272. if (userAgent.match(/JitsiMeetNW/)) {
  273. currentBrowser = RTCBrowserType.RTC_BROWSER_NWJS;
  274. const version = userAgent.match(/JitsiMeetNW\/([\d.]+)/)[1];
  275. logger.info(`This appears to be JitsiMeetNW, ver: ${version}`);
  276. return version;
  277. }
  278. return null;
  279. }
  280. /**
  281. *
  282. */
  283. function detectReactNative() {
  284. const match
  285. = navigator.userAgent.match(/\b(react[ \t_-]*native)(?:\/(\S+))?/i);
  286. let version;
  287. // If we're remote debugging a React Native app, it may be treated as
  288. // Chrome. Check navigator.product as well and always return some version
  289. // even if we can't get the real one.
  290. if (match || navigator.product === 'ReactNative') {
  291. currentBrowser = RTCBrowserType.RTC_BROWSER_REACT_NATIVE;
  292. let name;
  293. if (match && match.length > 2) {
  294. name = match[1];
  295. version = match[2];
  296. }
  297. name || (name = 'react-native');
  298. version || (version = 'unknown');
  299. console.info(`This appears to be ${name}, ver: ${version}`);
  300. } else {
  301. // We're not running in a React Native environment.
  302. version = null;
  303. }
  304. return version;
  305. }
  306. /**
  307. *
  308. */
  309. function detectBrowser() {
  310. let version;
  311. const detectors = [
  312. detectReactNative,
  313. detectElectron,
  314. detectNWJS,
  315. detectOpera,
  316. detectChrome,
  317. detectFirefox,
  318. detectIE,
  319. detectSafari
  320. ];
  321. // Try all browser detectors
  322. for (let i = 0; i < detectors.length; i++) {
  323. version = detectors[i]();
  324. if (version) {
  325. return version;
  326. }
  327. }
  328. logger.warn('Browser type defaults to Safari ver 1');
  329. currentBrowser = RTCBrowserType.RTC_BROWSER_SAFARI;
  330. return 1;
  331. }
  332. browserVersion = detectBrowser();
  333. module.exports = RTCBrowserType;