FeatureDetection.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. import defaultValue from "./defaultValue.js";
  2. import defer from "./defer.js";
  3. import defined from "./defined.js";
  4. import DeveloperError from "./DeveloperError.js";
  5. import Fullscreen from "./Fullscreen.js";
  6. let theNavigator;
  7. if (typeof navigator !== "undefined") {
  8. theNavigator = navigator;
  9. } else {
  10. theNavigator = {};
  11. }
  12. function extractVersion(versionString) {
  13. const parts = versionString.split(".");
  14. for (let i = 0, len = parts.length; i < len; ++i) {
  15. parts[i] = parseInt(parts[i], 10);
  16. }
  17. return parts;
  18. }
  19. let isChromeResult;
  20. let chromeVersionResult;
  21. function isChrome() {
  22. if (!defined(isChromeResult)) {
  23. isChromeResult = false;
  24. // Edge contains Chrome in the user agent too
  25. if (!isEdge()) {
  26. const fields = / Chrome\/([\.0-9]+)/.exec(theNavigator.userAgent);
  27. if (fields !== null) {
  28. isChromeResult = true;
  29. chromeVersionResult = extractVersion(fields[1]);
  30. }
  31. }
  32. }
  33. return isChromeResult;
  34. }
  35. function chromeVersion() {
  36. return isChrome() && chromeVersionResult;
  37. }
  38. let isSafariResult;
  39. let safariVersionResult;
  40. function isSafari() {
  41. if (!defined(isSafariResult)) {
  42. isSafariResult = false;
  43. // Chrome and Edge contain Safari in the user agent too
  44. if (
  45. !isChrome() &&
  46. !isEdge() &&
  47. / Safari\/[\.0-9]+/.test(theNavigator.userAgent)
  48. ) {
  49. const fields = / Version\/([\.0-9]+)/.exec(theNavigator.userAgent);
  50. if (fields !== null) {
  51. isSafariResult = true;
  52. safariVersionResult = extractVersion(fields[1]);
  53. }
  54. }
  55. }
  56. return isSafariResult;
  57. }
  58. function safariVersion() {
  59. return isSafari() && safariVersionResult;
  60. }
  61. let isWebkitResult;
  62. let webkitVersionResult;
  63. function isWebkit() {
  64. if (!defined(isWebkitResult)) {
  65. isWebkitResult = false;
  66. const fields = / AppleWebKit\/([\.0-9]+)(\+?)/.exec(theNavigator.userAgent);
  67. if (fields !== null) {
  68. isWebkitResult = true;
  69. webkitVersionResult = extractVersion(fields[1]);
  70. webkitVersionResult.isNightly = !!fields[2];
  71. }
  72. }
  73. return isWebkitResult;
  74. }
  75. function webkitVersion() {
  76. return isWebkit() && webkitVersionResult;
  77. }
  78. let isInternetExplorerResult;
  79. let internetExplorerVersionResult;
  80. function isInternetExplorer() {
  81. if (!defined(isInternetExplorerResult)) {
  82. isInternetExplorerResult = false;
  83. let fields;
  84. if (theNavigator.appName === "Microsoft Internet Explorer") {
  85. fields = /MSIE ([0-9]{1,}[\.0-9]{0,})/.exec(theNavigator.userAgent);
  86. if (fields !== null) {
  87. isInternetExplorerResult = true;
  88. internetExplorerVersionResult = extractVersion(fields[1]);
  89. }
  90. } else if (theNavigator.appName === "Netscape") {
  91. fields = /Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/.exec(
  92. theNavigator.userAgent
  93. );
  94. if (fields !== null) {
  95. isInternetExplorerResult = true;
  96. internetExplorerVersionResult = extractVersion(fields[1]);
  97. }
  98. }
  99. }
  100. return isInternetExplorerResult;
  101. }
  102. function internetExplorerVersion() {
  103. return isInternetExplorer() && internetExplorerVersionResult;
  104. }
  105. let isEdgeResult;
  106. let edgeVersionResult;
  107. function isEdge() {
  108. if (!defined(isEdgeResult)) {
  109. isEdgeResult = false;
  110. const fields = / Edge\/([\.0-9]+)/.exec(theNavigator.userAgent);
  111. if (fields !== null) {
  112. isEdgeResult = true;
  113. edgeVersionResult = extractVersion(fields[1]);
  114. }
  115. }
  116. return isEdgeResult;
  117. }
  118. function edgeVersion() {
  119. return isEdge() && edgeVersionResult;
  120. }
  121. let isFirefoxResult;
  122. let firefoxVersionResult;
  123. function isFirefox() {
  124. if (!defined(isFirefoxResult)) {
  125. isFirefoxResult = false;
  126. const fields = /Firefox\/([\.0-9]+)/.exec(theNavigator.userAgent);
  127. if (fields !== null) {
  128. isFirefoxResult = true;
  129. firefoxVersionResult = extractVersion(fields[1]);
  130. }
  131. }
  132. return isFirefoxResult;
  133. }
  134. let isWindowsResult;
  135. function isWindows() {
  136. if (!defined(isWindowsResult)) {
  137. isWindowsResult = /Windows/i.test(theNavigator.appVersion);
  138. }
  139. return isWindowsResult;
  140. }
  141. let isIPadOrIOSResult;
  142. function isIPadOrIOS() {
  143. if (!defined(isIPadOrIOSResult)) {
  144. isIPadOrIOSResult =
  145. navigator.platform === "iPhone" ||
  146. navigator.platform === "iPod" ||
  147. navigator.platform === "iPad";
  148. }
  149. return isIPadOrIOSResult;
  150. }
  151. function firefoxVersion() {
  152. return isFirefox() && firefoxVersionResult;
  153. }
  154. let hasPointerEvents;
  155. function supportsPointerEvents() {
  156. if (!defined(hasPointerEvents)) {
  157. //While navigator.pointerEnabled is deprecated in the W3C specification
  158. //we still need to use it if it exists in order to support browsers
  159. //that rely on it, such as the Windows WebBrowser control which defines
  160. //PointerEvent but sets navigator.pointerEnabled to false.
  161. //Firefox disabled because of https://github.com/CesiumGS/cesium/issues/6372
  162. hasPointerEvents =
  163. !isFirefox() &&
  164. typeof PointerEvent !== "undefined" &&
  165. (!defined(theNavigator.pointerEnabled) || theNavigator.pointerEnabled);
  166. }
  167. return hasPointerEvents;
  168. }
  169. let imageRenderingValueResult;
  170. let supportsImageRenderingPixelatedResult;
  171. function supportsImageRenderingPixelated() {
  172. if (!defined(supportsImageRenderingPixelatedResult)) {
  173. const canvas = document.createElement("canvas");
  174. canvas.setAttribute(
  175. "style",
  176. "image-rendering: -moz-crisp-edges;" + "image-rendering: pixelated;"
  177. );
  178. //canvas.style.imageRendering will be undefined, null or an empty string on unsupported browsers.
  179. const tmp = canvas.style.imageRendering;
  180. supportsImageRenderingPixelatedResult = defined(tmp) && tmp !== "";
  181. if (supportsImageRenderingPixelatedResult) {
  182. imageRenderingValueResult = tmp;
  183. }
  184. }
  185. return supportsImageRenderingPixelatedResult;
  186. }
  187. function imageRenderingValue() {
  188. return supportsImageRenderingPixelated()
  189. ? imageRenderingValueResult
  190. : undefined;
  191. }
  192. function supportsWebP() {
  193. //>>includeStart('debug', pragmas.debug);
  194. if (!supportsWebP.initialized) {
  195. throw new DeveloperError(
  196. "You must call FeatureDetection.supportsWebP.initialize and wait for the promise to resolve before calling FeatureDetection.supportsWebP"
  197. );
  198. }
  199. //>>includeEnd('debug');
  200. return supportsWebP._result;
  201. }
  202. supportsWebP._promise = undefined;
  203. supportsWebP._result = undefined;
  204. supportsWebP.initialize = function () {
  205. // From https://developers.google.com/speed/webp/faq#how_can_i_detect_browser_support_for_webp
  206. if (defined(supportsWebP._promise)) {
  207. return supportsWebP._promise;
  208. }
  209. const supportsWebPDeferred = defer();
  210. supportsWebP._promise = supportsWebPDeferred.promise;
  211. if (isEdge()) {
  212. // Edge's WebP support with WebGL is incomplete.
  213. // See bug report: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/19221241/
  214. supportsWebP._result = false;
  215. supportsWebPDeferred.resolve(supportsWebP._result);
  216. return supportsWebPDeferred.promise;
  217. }
  218. const image = new Image();
  219. image.onload = function () {
  220. supportsWebP._result = image.width > 0 && image.height > 0;
  221. supportsWebPDeferred.resolve(supportsWebP._result);
  222. };
  223. image.onerror = function () {
  224. supportsWebP._result = false;
  225. supportsWebPDeferred.resolve(supportsWebP._result);
  226. };
  227. image.src =
  228. "data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA";
  229. return supportsWebPDeferred.promise;
  230. };
  231. Object.defineProperties(supportsWebP, {
  232. initialized: {
  233. get: function () {
  234. return defined(supportsWebP._result);
  235. },
  236. },
  237. });
  238. const typedArrayTypes = [];
  239. if (typeof ArrayBuffer !== "undefined") {
  240. typedArrayTypes.push(
  241. Int8Array,
  242. Uint8Array,
  243. Int16Array,
  244. Uint16Array,
  245. Int32Array,
  246. Uint32Array,
  247. Float32Array,
  248. Float64Array
  249. );
  250. if (typeof Uint8ClampedArray !== "undefined") {
  251. typedArrayTypes.push(Uint8ClampedArray);
  252. }
  253. if (typeof Uint8ClampedArray !== "undefined") {
  254. typedArrayTypes.push(Uint8ClampedArray);
  255. }
  256. if (typeof BigInt64Array !== "undefined") {
  257. // eslint-disable-next-line no-undef
  258. typedArrayTypes.push(BigInt64Array);
  259. }
  260. if (typeof BigUint64Array !== "undefined") {
  261. // eslint-disable-next-line no-undef
  262. typedArrayTypes.push(BigUint64Array);
  263. }
  264. }
  265. /**
  266. * A set of functions to detect whether the current browser supports
  267. * various features.
  268. *
  269. * @namespace FeatureDetection
  270. */
  271. const FeatureDetection = {
  272. isChrome: isChrome,
  273. chromeVersion: chromeVersion,
  274. isSafari: isSafari,
  275. safariVersion: safariVersion,
  276. isWebkit: isWebkit,
  277. webkitVersion: webkitVersion,
  278. isInternetExplorer: isInternetExplorer,
  279. internetExplorerVersion: internetExplorerVersion,
  280. isEdge: isEdge,
  281. edgeVersion: edgeVersion,
  282. isFirefox: isFirefox,
  283. firefoxVersion: firefoxVersion,
  284. isWindows: isWindows,
  285. isIPadOrIOS: isIPadOrIOS,
  286. hardwareConcurrency: defaultValue(theNavigator.hardwareConcurrency, 3),
  287. supportsPointerEvents: supportsPointerEvents,
  288. supportsImageRenderingPixelated: supportsImageRenderingPixelated,
  289. supportsWebP: supportsWebP,
  290. imageRenderingValue: imageRenderingValue,
  291. typedArrayTypes: typedArrayTypes,
  292. };
  293. /**
  294. * Detects whether the current browser supports Basis Universal textures and the web assembly modules needed to transcode them.
  295. *
  296. * @param {Scene} scene
  297. * @returns {Boolean} true if the browser supports web assembly modules and the scene supports Basis Universal textures, false if not.
  298. */
  299. FeatureDetection.supportsBasis = function (scene) {
  300. return FeatureDetection.supportsWebAssembly() && scene.context.supportsBasis;
  301. };
  302. /**
  303. * Detects whether the current browser supports the full screen standard.
  304. *
  305. * @returns {Boolean} true if the browser supports the full screen standard, false if not.
  306. *
  307. * @see Fullscreen
  308. * @see {@link http://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html|W3C Fullscreen Living Specification}
  309. */
  310. FeatureDetection.supportsFullscreen = function () {
  311. return Fullscreen.supportsFullscreen();
  312. };
  313. /**
  314. * Detects whether the current browser supports typed arrays.
  315. *
  316. * @returns {Boolean} true if the browser supports typed arrays, false if not.
  317. *
  318. * @see {@link https://tc39.es/ecma262/#sec-typedarray-objects|Typed Array Specification}
  319. */
  320. FeatureDetection.supportsTypedArrays = function () {
  321. return typeof ArrayBuffer !== "undefined";
  322. };
  323. /**
  324. * Detects whether the current browser supports BigInt64Array typed arrays.
  325. *
  326. * @returns {Boolean} true if the browser supports BigInt64Array typed arrays, false if not.
  327. *
  328. * @see {@link https://tc39.es/ecma262/#sec-typedarray-objects|Typed Array Specification}
  329. */
  330. FeatureDetection.supportsBigInt64Array = function () {
  331. return typeof BigInt64Array !== "undefined";
  332. };
  333. /**
  334. * Detects whether the current browser supports BigUint64Array typed arrays.
  335. *
  336. * @returns {Boolean} true if the browser supports BigUint64Array typed arrays, false if not.
  337. *
  338. * @see {@link https://tc39.es/ecma262/#sec-typedarray-objects|Typed Array Specification}
  339. */
  340. FeatureDetection.supportsBigUint64Array = function () {
  341. return typeof BigUint64Array !== "undefined";
  342. };
  343. /**
  344. * Detects whether the current browser supports BigInt.
  345. *
  346. * @returns {Boolean} true if the browser supports BigInt, false if not.
  347. *
  348. * @see {@link https://tc39.es/ecma262/#sec-bigint-objects|BigInt Specification}
  349. */
  350. FeatureDetection.supportsBigInt = function () {
  351. return typeof BigInt !== "undefined";
  352. };
  353. /**
  354. * Detects whether the current browser supports Web Workers.
  355. *
  356. * @returns {Boolean} true if the browsers supports Web Workers, false if not.
  357. *
  358. * @see {@link http://www.w3.org/TR/workers/}
  359. */
  360. FeatureDetection.supportsWebWorkers = function () {
  361. return typeof Worker !== "undefined";
  362. };
  363. /**
  364. * Detects whether the current browser supports Web Assembly.
  365. *
  366. * @returns {Boolean} true if the browsers supports Web Assembly, false if not.
  367. *
  368. * @see {@link https://developer.mozilla.org/en-US/docs/WebAssembly}
  369. */
  370. FeatureDetection.supportsWebAssembly = function () {
  371. return typeof WebAssembly !== "undefined" && !FeatureDetection.isEdge();
  372. };
  373. export default FeatureDetection;