| 1 | /* |
| 2 | * noVNC: HTML5 VNC client |
| 3 | * Copyright (C) 2019 The noVNC Authors |
| 4 | * Licensed under MPL 2.0 (see LICENSE.txt) |
| 5 | * |
| 6 | * See README.md for usage and integration instructions. |
| 7 | * |
| 8 | * Browser feature support detection |
| 9 | */ |
| 10 | |
| 11 | import * as Log from './logging.js'; |
| 12 | |
| 13 | // Touch detection |
| 14 | export let isTouchDevice = ('ontouchstart' in document.documentElement) || |
| 15 | // requried for Chrome debugger |
| 16 | (document.ontouchstart !== undefined) || |
| 17 | // required for MS Surface |
| 18 | (navigator.maxTouchPoints > 0) || |
| 19 | (navigator.msMaxTouchPoints > 0); |
| 20 | window.addEventListener('touchstart', function onFirstTouch() { |
| 21 | isTouchDevice = true; |
| 22 | window.removeEventListener('touchstart', onFirstTouch, false); |
| 23 | }, false); |
| 24 | |
| 25 | |
| 26 | // The goal is to find a certain physical width, the devicePixelRatio |
| 27 | // brings us a bit closer but is not optimal. |
| 28 | export let dragThreshold = 10 * (window.devicePixelRatio || 1); |
| 29 | |
| 30 | let _supportsCursorURIs = false; |
| 31 | |
| 32 | try { |
| 33 | const target = document.createElement('canvas'); |
| 34 | target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default'; |
| 35 | |
| 36 | if (target.style.cursor.indexOf("url") === 0) { |
| 37 | Log.Info("Data URI scheme cursor supported"); |
| 38 | _supportsCursorURIs = true; |
| 39 | } else { |
| 40 | Log.Warn("Data URI scheme cursor not supported"); |
| 41 | } |
| 42 | } catch (exc) { |
| 43 | Log.Error("Data URI scheme cursor test exception: " + exc); |
| 44 | } |
| 45 | |
| 46 | export const supportsCursorURIs = _supportsCursorURIs; |
| 47 | |
| 48 | let _hasScrollbarGutter = true; |
| 49 | try { |
| 50 | // Create invisible container |
| 51 | const container = document.createElement('div'); |
| 52 | container.style.visibility = 'hidden'; |
| 53 | container.style.overflow = 'scroll'; // forcing scrollbars |
| 54 | document.body.appendChild(container); |
| 55 | |
| 56 | // Create a div and place it in the container |
| 57 | const child = document.createElement('div'); |
| 58 | container.appendChild(child); |
| 59 | |
| 60 | // Calculate the difference between the container's full width |
| 61 | // and the child's width - the difference is the scrollbars |
| 62 | const scrollbarWidth = (container.offsetWidth - child.offsetWidth); |
| 63 | |
| 64 | // Clean up |
| 65 | container.parentNode.removeChild(container); |
| 66 | |
| 67 | _hasScrollbarGutter = scrollbarWidth != 0; |
| 68 | } catch (exc) { |
| 69 | Log.Error("Scrollbar test exception: " + exc); |
| 70 | } |
| 71 | export const hasScrollbarGutter = _hasScrollbarGutter; |
| 72 | |
| 73 | /* |
| 74 | * The functions for detection of platforms and browsers below are exported |
| 75 | * but the use of these should be minimized as much as possible. |
| 76 | * |
| 77 | * It's better to use feature detection than platform detection. |
| 78 | */ |
| 79 | |
| 80 | /* OS */ |
| 81 | |
| 82 | export function isMac() { |
| 83 | return !!(/mac/i).exec(navigator.platform); |
| 84 | } |
| 85 | |
| 86 | export function isWindows() { |
| 87 | return !!(/win/i).exec(navigator.platform); |
| 88 | } |
| 89 | |
| 90 | export function isIOS() { |
| 91 | return (!!(/ipad/i).exec(navigator.platform) || |
| 92 | !!(/iphone/i).exec(navigator.platform) || |
| 93 | !!(/ipod/i).exec(navigator.platform)); |
| 94 | } |
| 95 | |
| 96 | export function isAndroid() { |
| 97 | /* Android sets navigator.platform to Linux :/ */ |
| 98 | return !!navigator.userAgent.match('Android '); |
| 99 | } |
| 100 | |
| 101 | export function isChromeOS() { |
| 102 | /* ChromeOS sets navigator.platform to Linux :/ */ |
| 103 | return !!navigator.userAgent.match(' CrOS '); |
| 104 | } |
| 105 | |
| 106 | /* Browser */ |
| 107 | |
| 108 | export function isSafari() { |
| 109 | return !!navigator.userAgent.match('Safari/...') && |
| 110 | !navigator.userAgent.match('Chrome/...') && |
| 111 | !navigator.userAgent.match('Chromium/...') && |
| 112 | !navigator.userAgent.match('Epiphany/...'); |
| 113 | } |
| 114 | |
| 115 | export function isFirefox() { |
| 116 | return !!navigator.userAgent.match('Firefox/...') && |
| 117 | !navigator.userAgent.match('Seamonkey/...'); |
| 118 | } |
| 119 | |
| 120 | export function isChrome() { |
| 121 | return !!navigator.userAgent.match('Chrome/...') && |
| 122 | !navigator.userAgent.match('Chromium/...') && |
| 123 | !navigator.userAgent.match('Edg/...') && |
| 124 | !navigator.userAgent.match('OPR/...'); |
| 125 | } |
| 126 | |
| 127 | export function isChromium() { |
| 128 | return !!navigator.userAgent.match('Chromium/...'); |
| 129 | } |
| 130 | |
| 131 | export function isOpera() { |
| 132 | return !!navigator.userAgent.match('OPR/...'); |
| 133 | } |
| 134 | |
| 135 | export function isEdge() { |
| 136 | return !!navigator.userAgent.match('Edg/...'); |
| 137 | } |
| 138 | |
| 139 | /* Engine */ |
| 140 | |
| 141 | export function isGecko() { |
| 142 | return !!navigator.userAgent.match('Gecko/...'); |
| 143 | } |
| 144 | |
| 145 | export function isWebKit() { |
| 146 | return !!navigator.userAgent.match('AppleWebKit/...') && |
| 147 | !navigator.userAgent.match('Chrome/...'); |
| 148 | } |
| 149 | |
| 150 | export function isBlink() { |
| 151 | return !!navigator.userAgent.match('Chrome/...'); |
| 152 | } |