/** * @license * Copyright The Closure Library Authors. * SPDX-License-Identifier: Apache-2.0 */ /** * @fileoverview Rendering engine detection. * @see User agent strings * For information on the browser brand (such as Safari versus Chrome), see * goog.userAgent.product. * @see ../demos/useragent.html */ goog.provide('goog.userAgent'); goog.require('goog.labs.userAgent.browser'); goog.require('goog.labs.userAgent.engine'); goog.require('goog.labs.userAgent.platform'); goog.require('goog.labs.userAgent.util'); goog.require('goog.reflect'); goog.require('goog.string.internal'); /** * @define {boolean} Whether we know at compile-time that the browser is IE. */ goog.userAgent.ASSUME_IE = goog.define('goog.userAgent.ASSUME_IE', false); /** * @define {boolean} Whether we know at compile-time that the browser is EDGE, * referring to EdgeHTML based Edge. */ goog.userAgent.ASSUME_EDGE = goog.define('goog.userAgent.ASSUME_EDGE', false); /** * @define {boolean} Whether we know at compile-time that the browser is GECKO. */ goog.userAgent.ASSUME_GECKO = goog.define('goog.userAgent.ASSUME_GECKO', false); /** * @define {boolean} Whether we know at compile-time that the browser is WEBKIT. */ goog.userAgent.ASSUME_WEBKIT = goog.define('goog.userAgent.ASSUME_WEBKIT', false); /** * @define {boolean} Whether we know at compile-time that the browser is a * mobile device running WebKit e.g. iPhone or Android. */ goog.userAgent.ASSUME_MOBILE_WEBKIT = goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false); /** * @define {boolean} Whether we know at compile-time that the browser is OPERA, * referring to Presto-based Opera. */ goog.userAgent.ASSUME_OPERA = goog.define('goog.userAgent.ASSUME_OPERA', false); /** * @define {boolean} Whether the * `goog.userAgent.isVersionOrHigher` * function will return true for any version. */ goog.userAgent.ASSUME_ANY_VERSION = goog.define('goog.userAgent.ASSUME_ANY_VERSION', false); /** * Whether we know the browser engine at compile-time. * @type {boolean} * @private */ goog.userAgent.BROWSER_KNOWN_ = goog.userAgent.ASSUME_IE || goog.userAgent.ASSUME_EDGE || goog.userAgent.ASSUME_GECKO || goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_OPERA; /** * Returns the userAgent string for the current browser. * * @return {string} The userAgent string. */ goog.userAgent.getUserAgentString = function() { 'use strict'; return goog.labs.userAgent.util.getUserAgent(); }; /** * @return {?Navigator} The native navigator object. */ goog.userAgent.getNavigatorTyped = function() { 'use strict'; // Need a local navigator reference instead of using the global one, // to avoid the rare case where they reference different objects. // (in a WorkerPool, for example). return goog.global['navigator'] || null; }; /** * TODO(nnaze): Change type to "Navigator" and update compilation targets. * @return {?Object} The native navigator object. */ goog.userAgent.getNavigator = function() { 'use strict'; return goog.userAgent.getNavigatorTyped(); }; /** * Whether the user agent is Presto-based Opera. * @type {boolean} */ goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ? goog.userAgent.ASSUME_OPERA : goog.labs.userAgent.browser.isOpera(); /** * Whether the user agent is Internet Explorer. * @type {boolean} */ goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ? goog.userAgent.ASSUME_IE : goog.labs.userAgent.browser.isIE(); /** * Whether the user agent is Microsoft Edge (EdgeHTML based). * @type {boolean} */ goog.userAgent.EDGE = goog.userAgent.BROWSER_KNOWN_ ? goog.userAgent.ASSUME_EDGE : goog.labs.userAgent.engine.isEdge(); /** * Whether the user agent is MS Internet Explorer or MS Edge (EdgeHTML based). * @type {boolean} */ goog.userAgent.EDGE_OR_IE = goog.userAgent.EDGE || goog.userAgent.IE; /** * Whether the user agent is Gecko. Gecko is the rendering engine used by * Mozilla, Firefox, and others. * @type {boolean} */ goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ? goog.userAgent.ASSUME_GECKO : goog.labs.userAgent.engine.isGecko(); /** * Whether the user agent is WebKit. WebKit is the rendering engine that * Safari, Edge Chromium, Opera Chromium, Android and others use. * @type {boolean} */ goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ? goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT : goog.labs.userAgent.engine.isWebKit(); /** * Whether the user agent is running on a mobile device. * * This is a separate function so that the logic can be tested. * * TODO(nnaze): Investigate swapping in goog.labs.userAgent.device.isMobile(). * * @return {boolean} Whether the user agent is running on a mobile device. * @private */ goog.userAgent.isMobile_ = function() { 'use strict'; return goog.userAgent.WEBKIT && goog.labs.userAgent.util.matchUserAgent('Mobile'); }; /** * Whether the user agent is running on a mobile device. * * TODO(nnaze): Consider deprecating MOBILE when labs.userAgent * is promoted as the gecko/webkit logic is likely inaccurate. * * @type {boolean} */ goog.userAgent.MOBILE = goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.isMobile_(); /** * Used while transitioning code to use WEBKIT instead. * @type {boolean} * @deprecated Use {@link goog.userAgent.product.SAFARI} instead. * TODO(nicksantos): Delete this from goog.userAgent. */ goog.userAgent.SAFARI = goog.userAgent.WEBKIT; /** * @return {string} the platform (operating system) the user agent is running * on. Default to empty string because navigator.platform may not be defined * (on Rhino, for example). * @private */ goog.userAgent.determinePlatform_ = function() { 'use strict'; var navigator = goog.userAgent.getNavigatorTyped(); return navigator && navigator.platform || ''; }; /** * The platform (operating system) the user agent is running on. Default to * empty string because navigator.platform may not be defined (on Rhino, for * example). * @type {string} */ goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_(); /** * @define {boolean} Whether the user agent is running on a Macintosh operating * system. */ goog.userAgent.ASSUME_MAC = goog.define('goog.userAgent.ASSUME_MAC', false); /** * @define {boolean} Whether the user agent is running on a Windows operating * system. */ goog.userAgent.ASSUME_WINDOWS = goog.define('goog.userAgent.ASSUME_WINDOWS', false); /** * @define {boolean} Whether the user agent is running on a Linux operating * system. */ goog.userAgent.ASSUME_LINUX = goog.define('goog.userAgent.ASSUME_LINUX', false); /** * @define {boolean} Whether the user agent is running on a X11 windowing * system. */ goog.userAgent.ASSUME_X11 = goog.define('goog.userAgent.ASSUME_X11', false); /** * @define {boolean} Whether the user agent is running on Android. */ goog.userAgent.ASSUME_ANDROID = goog.define('goog.userAgent.ASSUME_ANDROID', false); /** * @define {boolean} Whether the user agent is running on an iPhone. */ goog.userAgent.ASSUME_IPHONE = goog.define('goog.userAgent.ASSUME_IPHONE', false); /** * @define {boolean} Whether the user agent is running on an iPad. */ goog.userAgent.ASSUME_IPAD = goog.define('goog.userAgent.ASSUME_IPAD', false); /** * @define {boolean} Whether the user agent is running on an iPod. */ goog.userAgent.ASSUME_IPOD = goog.define('goog.userAgent.ASSUME_IPOD', false); /** * @define {boolean} Whether the user agent is running on KaiOS. */ goog.userAgent.ASSUME_KAIOS = goog.define('goog.userAgent.ASSUME_KAIOS', false); /** * @type {boolean} * @private */ goog.userAgent.PLATFORM_KNOWN_ = goog.userAgent.ASSUME_MAC || goog.userAgent.ASSUME_WINDOWS || goog.userAgent.ASSUME_LINUX || goog.userAgent.ASSUME_X11 || goog.userAgent.ASSUME_ANDROID || goog.userAgent.ASSUME_IPHONE || goog.userAgent.ASSUME_IPAD || goog.userAgent.ASSUME_IPOD; /** * Whether the user agent is running on a Macintosh operating system. * @type {boolean} */ goog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ? goog.userAgent.ASSUME_MAC : goog.labs.userAgent.platform.isMacintosh(); /** * Whether the user agent is running on a Windows operating system. * @type {boolean} */ goog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ? goog.userAgent.ASSUME_WINDOWS : goog.labs.userAgent.platform.isWindows(); /** * Whether the user agent is Linux per the legacy behavior of * goog.userAgent.LINUX, which considered ChromeOS to also be * Linux. * @return {boolean} * @private */ goog.userAgent.isLegacyLinux_ = function() { 'use strict'; return goog.labs.userAgent.platform.isLinux() || goog.labs.userAgent.platform.isChromeOS(); }; /** * Whether the user agent is running on a Linux operating system. * * Note that goog.userAgent.LINUX considers ChromeOS to be Linux, * while goog.labs.userAgent.platform considers ChromeOS and * Linux to be different OSes. * * @type {boolean} */ goog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ? goog.userAgent.ASSUME_LINUX : goog.userAgent.isLegacyLinux_(); /** * @return {boolean} Whether the user agent is an X11 windowing system. * @private */ goog.userAgent.isX11_ = function() { 'use strict'; var navigator = goog.userAgent.getNavigatorTyped(); return !!navigator && goog.string.internal.contains(navigator['appVersion'] || '', 'X11'); }; /** * Whether the user agent is running on a X11 windowing system. * @type {boolean} */ goog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ? goog.userAgent.ASSUME_X11 : goog.userAgent.isX11_(); /** * Whether the user agent is running on Android. * @type {boolean} */ goog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ? goog.userAgent.ASSUME_ANDROID : goog.labs.userAgent.platform.isAndroid(); /** * Whether the user agent is running on an iPhone. * @type {boolean} */ goog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ? goog.userAgent.ASSUME_IPHONE : goog.labs.userAgent.platform.isIphone(); /** * Whether the user agent is running on an iPad. * @type {boolean} */ goog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ? goog.userAgent.ASSUME_IPAD : goog.labs.userAgent.platform.isIpad(); /** * Whether the user agent is running on an iPod. * @type {boolean} */ goog.userAgent.IPOD = goog.userAgent.PLATFORM_KNOWN_ ? goog.userAgent.ASSUME_IPOD : goog.labs.userAgent.platform.isIpod(); /** * Whether the user agent is running on iOS. * @type {boolean} */ goog.userAgent.IOS = goog.userAgent.PLATFORM_KNOWN_ ? (goog.userAgent.ASSUME_IPHONE || goog.userAgent.ASSUME_IPAD || goog.userAgent.ASSUME_IPOD) : goog.labs.userAgent.platform.isIos(); /** * Whether the user agent is running on KaiOS. * @type {boolean} */ goog.userAgent.KAIOS = goog.userAgent.PLATFORM_KNOWN_ ? goog.userAgent.ASSUME_KAIOS : goog.labs.userAgent.platform.isKaiOS(); /** * @return {string} The string that describes the version number of the user * agent. * @private */ goog.userAgent.determineVersion_ = function() { 'use strict'; // All browsers have different ways to detect the version and they all have // different naming schemes. // version is a string rather than a number because it may contain 'b', 'a', // and so on. var version = ''; var arr = goog.userAgent.getVersionRegexResult_(); if (arr) { version = arr ? arr[1] : ''; } if (goog.userAgent.IE) { // IE9 can be in document mode 9 but be reporting an inconsistent user agent // version. If it is identifying as a version lower than 9 we take the // documentMode as the version instead. IE8 has similar behavior. // It is recommended to set the X-UA-Compatible header to ensure that IE9 // uses documentMode 9. var docMode = goog.userAgent.getDocumentMode_(); if (docMode != null && docMode > parseFloat(version)) { return String(docMode); } } return version; }; /** * @return {?IArrayLike|undefined} The version regex matches from * parsing the user * agent string. These regex statements must be executed inline so they can * be compiled out by the closure compiler with the rest of the useragent * detection logic when ASSUME_* is specified. * @private */ goog.userAgent.getVersionRegexResult_ = function() { 'use strict'; var userAgent = goog.userAgent.getUserAgentString(); if (goog.userAgent.GECKO) { return /rv\:([^\);]+)(\)|;)/.exec(userAgent); } if (goog.userAgent.EDGE) { return /Edge\/([\d\.]+)/.exec(userAgent); } if (goog.userAgent.IE) { return /\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(userAgent); } if (goog.userAgent.WEBKIT) { // WebKit/125.4 return /WebKit\/(\S+)/.exec(userAgent); } if (goog.userAgent.OPERA) { // If none of the above browsers were detected but the browser is Opera, the // only string that is of interest is 'Version/'. return /(?:Version)[ \/]?(\S+)/.exec(userAgent); } return undefined; }; /** * @return {number|undefined} Returns the document mode (for testing). * @private */ goog.userAgent.getDocumentMode_ = function() { 'use strict'; // NOTE(user): goog.userAgent may be used in context where there is no DOM. var doc = goog.global['document']; return doc ? doc['documentMode'] : undefined; }; /** * The version of the user agent. This is a string because it might contain * 'b' (as in beta) as well as multiple dots. * @type {string} */ goog.userAgent.VERSION = goog.userAgent.determineVersion_(); /** * Compares two version numbers. * * @param {string} v1 Version of first item. * @param {string} v2 Version of second item. * * @return {number} 1 if first argument is higher * 0 if arguments are equal * -1 if second argument is higher. * @deprecated Use goog.string.compareVersions. */ goog.userAgent.compare = function(v1, v2) { 'use strict'; return goog.string.internal.compareVersions(v1, v2); }; /** * Cache for {@link goog.userAgent.isVersionOrHigher}. * Calls to compareVersions are surprisingly expensive and, as a browser's * version number is unlikely to change during a session, we cache the results. * @const * @private */ goog.userAgent.isVersionOrHigherCache_ = {}; /** * Whether the user agent version is higher or the same as the given version. * NOTE: When checking the version numbers for Firefox and Safari, be sure to * use the engine's version, not the browser's version number. For example, * Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11. * Opera and Internet Explorer versions match the product release number.
* @see * Webkit * @see Gecko * * @param {string|number} version The version to check. * @return {boolean} Whether the user agent version is higher or the same as * the given version. */ goog.userAgent.isVersionOrHigher = function(version) { 'use strict'; return goog.userAgent.ASSUME_ANY_VERSION || goog.reflect.cache( goog.userAgent.isVersionOrHigherCache_, version, function() { 'use strict'; return goog.string.internal.compareVersions( goog.userAgent.VERSION, version) >= 0; }); }; /** * Whether the IE effective document mode is higher or the same as the given * document mode version. * NOTE: Only for IE, return false for another browser. * * @param {number} documentMode The document mode version to check. * @return {boolean} Whether the IE effective document mode is higher or the * same as the given version. */ goog.userAgent.isDocumentModeOrHigher = function(documentMode) { 'use strict'; return Number(goog.userAgent.DOCUMENT_MODE) >= documentMode; }; /** * Deprecated alias to `goog.userAgent.isDocumentModeOrHigher`. * @param {number} version The version to check. * @return {boolean} Whether the IE effective document mode is higher or the * same as the given version. * @deprecated Use goog.userAgent.isDocumentModeOrHigher(). */ goog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher; /** * For IE version < 7, documentMode is undefined, so attempt to use the * CSS1Compat property to see if we are in standards mode. If we are in * standards mode, treat the browser version as the document mode. Otherwise, * IE is emulating version 5. * * NOTE(user): Support for IE < 7 is long gone, so this is now simplified. * It returns document.documentMode for IE and undefined for everything else. * * @type {number|undefined} * @const */ goog.userAgent.DOCUMENT_MODE = (function() { 'use strict'; var doc = goog.global['document']; if (!doc || !goog.userAgent.IE) return undefined; // This must be an IE user agent. var documentMode = goog.userAgent.getDocumentMode_(); if (documentMode) return documentMode; // The user agent version string begins with the major version. // Parse the major version and truncate anything following. var ieVersion = parseInt(goog.userAgent.VERSION, 10); return ieVersion || undefined; })();