// Copyright 2006 The Closure Library Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview Rendering engine detection. * @see User agent strings * For information on the browser brand (such as Safari versus Chrome), see * goog.userAgent.product. * @author arv@google.com (Erik Arvidsson) * @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.string'); /** * @define {boolean} Whether we know at compile-time that the browser is IE. */ goog.define('goog.userAgent.ASSUME_IE', false); /** * @define {boolean} Whether we know at compile-time that the browser is EDGE. */ goog.define('goog.userAgent.ASSUME_EDGE', false); /** * @define {boolean} Whether we know at compile-time that the browser is GECKO. */ goog.define('goog.userAgent.ASSUME_GECKO', false); /** * @define {boolean} Whether we know at compile-time that the browser is 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.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false); /** * @define {boolean} Whether we know at compile-time that the browser is OPERA. */ goog.define('goog.userAgent.ASSUME_OPERA', false); /** * @define {boolean} Whether the * {@code goog.userAgent.isVersionOrHigher} * function will return true for 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() { return goog.labs.userAgent.util.getUserAgent(); }; /** * TODO(nnaze): Change type to "Navigator" and update compilation targets. * @return {Object} The native navigator object. */ goog.userAgent.getNavigator = function() { // 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; }; /** * Whether the user agent is 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. * @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. * @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, 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() { 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() { var navigator = goog.userAgent.getNavigator(); 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.define('goog.userAgent.ASSUME_MAC', false); /** * @define {boolean} Whether the user agent is running on a Windows operating * system. */ goog.define('goog.userAgent.ASSUME_WINDOWS', false); /** * @define {boolean} Whether the user agent is running on a Linux operating * system. */ goog.define('goog.userAgent.ASSUME_LINUX', false); /** * @define {boolean} Whether the user agent is running on a X11 windowing * system. */ goog.define('goog.userAgent.ASSUME_X11', false); /** * @define {boolean} Whether the user agent is running on Android. */ goog.define('goog.userAgent.ASSUME_ANDROID', false); /** * @define {boolean} Whether the user agent is running on an iPhone. */ goog.define('goog.userAgent.ASSUME_IPHONE', false); /** * @define {boolean} Whether the user agent is running on an iPad. */ goog.define('goog.userAgent.ASSUME_IPAD', false); /** * @define {boolean} Whether the user agent is running on an iPod. */ goog.define('goog.userAgent.ASSUME_IPOD', 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() { 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() { var navigator = goog.userAgent.getNavigator(); return !!navigator && goog.string.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(); /** * @return {string} The string that describes the version number of the user * agent. * @private */ goog.userAgent.determineVersion_ = function() { // 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 {?Array|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() { 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() { // 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) { return goog.string.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) { return goog.userAgent.ASSUME_ANY_VERSION || goog.userAgent.isVersionOrHigherCache_[version] || (goog.userAgent.isVersionOrHigherCache_[version] = goog.string.compareVersions(goog.userAgent.VERSION, version) >= 0); }; /** * Deprecated alias to {@code goog.userAgent.isVersionOrHigher}. * @param {string|number} version The version to check. * @return {boolean} Whether the user agent version is higher or the same as * the given version. * @deprecated Use goog.userAgent.isVersionOrHigher(). */ goog.userAgent.isVersion = goog.userAgent.isVersionOrHigher; /** * 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) { return Number(goog.userAgent.DOCUMENT_MODE) >= documentMode; }; /** * Deprecated alias to {@code 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. * @type {number|undefined} * @const */ goog.userAgent.DOCUMENT_MODE = (function() { var doc = goog.global['document']; var mode = goog.userAgent.getDocumentMode_(); if (!doc || !goog.userAgent.IE) { return undefined; } return mode || (doc['compatMode'] == 'CSS1Compat' ? parseInt(goog.userAgent.VERSION, 10) : 5); })();