capabilities.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. /* global AUTH_CONFIG */
  2. import { browserName, locale } from './utils';
  3. async function checkCrypto() {
  4. try {
  5. const key = await crypto.subtle.generateKey(
  6. {
  7. name: 'AES-GCM',
  8. length: 128
  9. },
  10. true,
  11. ['encrypt', 'decrypt']
  12. );
  13. await crypto.subtle.exportKey('raw', key);
  14. await crypto.subtle.encrypt(
  15. {
  16. name: 'AES-GCM',
  17. iv: crypto.getRandomValues(new Uint8Array(12)),
  18. tagLength: 128
  19. },
  20. key,
  21. new ArrayBuffer(8)
  22. );
  23. await crypto.subtle.importKey(
  24. 'raw',
  25. crypto.getRandomValues(new Uint8Array(16)),
  26. 'PBKDF2',
  27. false,
  28. ['deriveKey']
  29. );
  30. await crypto.subtle.importKey(
  31. 'raw',
  32. crypto.getRandomValues(new Uint8Array(16)),
  33. 'HKDF',
  34. false,
  35. ['deriveKey']
  36. );
  37. await crypto.subtle.generateKey(
  38. {
  39. name: 'ECDH',
  40. namedCurve: 'P-256'
  41. },
  42. true,
  43. ['deriveBits']
  44. );
  45. return true;
  46. } catch (err) {
  47. try {
  48. window.asmCrypto = await import('asmcrypto.js');
  49. await import('@dannycoates/webcrypto-liner/build/shim');
  50. return true;
  51. } catch (e) {
  52. return false;
  53. }
  54. }
  55. }
  56. function checkStreams() {
  57. try {
  58. new ReadableStream({
  59. pull() {}
  60. });
  61. return true;
  62. } catch (e) {
  63. return false;
  64. }
  65. }
  66. async function polyfillStreams() {
  67. try {
  68. await import('@mattiasbuelens/web-streams-polyfill');
  69. return true;
  70. } catch (e) {
  71. return false;
  72. }
  73. }
  74. export default async function getCapabilities() {
  75. const browser = browserName();
  76. const isMobile = /mobi|android/i.test(navigator.userAgent);
  77. const serviceWorker = 'serviceWorker' in navigator && browser !== 'edge';
  78. let crypto = await checkCrypto();
  79. const nativeStreams = checkStreams();
  80. let polyStreams = false;
  81. if (!nativeStreams) {
  82. polyStreams = await polyfillStreams();
  83. }
  84. let account = typeof AUTH_CONFIG !== 'undefined';
  85. try {
  86. account = account && !!localStorage;
  87. } catch (e) {
  88. account = false;
  89. }
  90. const share =
  91. isMobile &&
  92. typeof navigator.share === 'function' &&
  93. locale().startsWith('en'); // en until strings merge
  94. const standalone =
  95. window.matchMedia('(display-mode: standalone)').matches ||
  96. navigator.standalone;
  97. const mobileFirefox = browser === 'firefox' && isMobile;
  98. return {
  99. account,
  100. crypto,
  101. serviceWorker,
  102. streamUpload: nativeStreams || polyStreams,
  103. streamDownload:
  104. nativeStreams && serviceWorker && browser !== 'safari' && !mobileFirefox,
  105. multifile: nativeStreams || polyStreams,
  106. share,
  107. standalone
  108. };
  109. }