browser.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import { Emitter } from '../common/event.js';
  6. import { Disposable, markAsSingleton } from '../common/lifecycle.js';
  7. class WindowManager {
  8. constructor() {
  9. // --- Zoom Factor
  10. this._zoomFactor = 1;
  11. }
  12. getZoomFactor() {
  13. return this._zoomFactor;
  14. }
  15. }
  16. WindowManager.INSTANCE = new WindowManager();
  17. /**
  18. * See https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#monitoring_screen_resolution_or_zoom_level_changes
  19. */
  20. class DevicePixelRatioMonitor extends Disposable {
  21. constructor() {
  22. super();
  23. this._onDidChange = this._register(new Emitter());
  24. this.onDidChange = this._onDidChange.event;
  25. this._listener = () => this._handleChange(true);
  26. this._mediaQueryList = null;
  27. this._handleChange(false);
  28. }
  29. _handleChange(fireEvent) {
  30. var _a;
  31. (_a = this._mediaQueryList) === null || _a === void 0 ? void 0 : _a.removeEventListener('change', this._listener);
  32. this._mediaQueryList = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
  33. this._mediaQueryList.addEventListener('change', this._listener);
  34. if (fireEvent) {
  35. this._onDidChange.fire();
  36. }
  37. }
  38. }
  39. class PixelRatioImpl extends Disposable {
  40. get value() {
  41. return this._value;
  42. }
  43. constructor() {
  44. super();
  45. this._onDidChange = this._register(new Emitter());
  46. this.onDidChange = this._onDidChange.event;
  47. this._value = this._getPixelRatio();
  48. const dprMonitor = this._register(new DevicePixelRatioMonitor());
  49. this._register(dprMonitor.onDidChange(() => {
  50. this._value = this._getPixelRatio();
  51. this._onDidChange.fire(this._value);
  52. }));
  53. }
  54. _getPixelRatio() {
  55. const ctx = document.createElement('canvas').getContext('2d');
  56. const dpr = window.devicePixelRatio || 1;
  57. const bsr = ctx.webkitBackingStorePixelRatio ||
  58. ctx.mozBackingStorePixelRatio ||
  59. ctx.msBackingStorePixelRatio ||
  60. ctx.oBackingStorePixelRatio ||
  61. ctx.backingStorePixelRatio || 1;
  62. return dpr / bsr;
  63. }
  64. }
  65. class PixelRatioFacade {
  66. constructor() {
  67. this._pixelRatioMonitor = null;
  68. }
  69. _getOrCreatePixelRatioMonitor() {
  70. if (!this._pixelRatioMonitor) {
  71. this._pixelRatioMonitor = markAsSingleton(new PixelRatioImpl());
  72. }
  73. return this._pixelRatioMonitor;
  74. }
  75. /**
  76. * Get the current value.
  77. */
  78. get value() {
  79. return this._getOrCreatePixelRatioMonitor().value;
  80. }
  81. /**
  82. * Listen for changes.
  83. */
  84. get onDidChange() {
  85. return this._getOrCreatePixelRatioMonitor().onDidChange;
  86. }
  87. }
  88. export function addMatchMediaChangeListener(query, callback) {
  89. if (typeof query === 'string') {
  90. query = window.matchMedia(query);
  91. }
  92. query.addEventListener('change', callback);
  93. }
  94. /**
  95. * Returns the pixel ratio.
  96. *
  97. * This is useful for rendering <canvas> elements at native screen resolution or for being used as
  98. * a cache key when storing font measurements. Fonts might render differently depending on resolution
  99. * and any measurements need to be discarded for example when a window is moved from a monitor to another.
  100. */
  101. export const PixelRatio = new PixelRatioFacade();
  102. /** The zoom scale for an index, e.g. 1, 1.2, 1.4 */
  103. export function getZoomFactor() {
  104. return WindowManager.INSTANCE.getZoomFactor();
  105. }
  106. const userAgent = navigator.userAgent;
  107. export const isFirefox = (userAgent.indexOf('Firefox') >= 0);
  108. export const isWebKit = (userAgent.indexOf('AppleWebKit') >= 0);
  109. export const isChrome = (userAgent.indexOf('Chrome') >= 0);
  110. export const isSafari = (!isChrome && (userAgent.indexOf('Safari') >= 0));
  111. export const isWebkitWebView = (!isChrome && !isSafari && isWebKit);
  112. export const isElectron = (userAgent.indexOf('Electron/') >= 0);
  113. export const isAndroid = (userAgent.indexOf('Android') >= 0);
  114. let standalone = false;
  115. if (window.matchMedia) {
  116. const standaloneMatchMedia = window.matchMedia('(display-mode: standalone) or (display-mode: window-controls-overlay)');
  117. const fullScreenMatchMedia = window.matchMedia('(display-mode: fullscreen)');
  118. standalone = standaloneMatchMedia.matches;
  119. addMatchMediaChangeListener(standaloneMatchMedia, ({ matches }) => {
  120. // entering fullscreen would change standaloneMatchMedia.matches to false
  121. // if standalone is true (running as PWA) and entering fullscreen, skip this change
  122. if (standalone && fullScreenMatchMedia.matches) {
  123. return;
  124. }
  125. // otherwise update standalone (browser to PWA or PWA to browser)
  126. standalone = matches;
  127. });
  128. }
  129. export function isStandalone() {
  130. return standalone;
  131. }