Path: blob/trunk/third_party/closure/goog/useragent/useragent.js
4504 views
/**1* @license2* Copyright The Closure Library Authors.3* SPDX-License-Identifier: Apache-2.04*/56/**7* @fileoverview Rendering engine detection.8* @see <a href="http://www.useragentstring.com/">User agent strings</a>9* For information on the browser brand (such as Safari versus Chrome), see10* goog.userAgent.product.11* @see ../demos/useragent.html12*/1314goog.provide('goog.userAgent');1516goog.require('goog.labs.userAgent.browser');17goog.require('goog.labs.userAgent.engine');18goog.require('goog.labs.userAgent.platform');19goog.require('goog.labs.userAgent.util');20goog.require('goog.reflect');21goog.require('goog.string.internal');222324/**25* @define {boolean} Whether we know at compile-time that the browser is IE.26*/27goog.userAgent.ASSUME_IE = goog.define('goog.userAgent.ASSUME_IE', false);282930/**31* @define {boolean} Whether we know at compile-time that the browser is EDGE,32* referring to EdgeHTML based Edge.33*/34goog.userAgent.ASSUME_EDGE = goog.define('goog.userAgent.ASSUME_EDGE', false);353637/**38* @define {boolean} Whether we know at compile-time that the browser is GECKO.39*/40goog.userAgent.ASSUME_GECKO = goog.define('goog.userAgent.ASSUME_GECKO', false);414243/**44* @define {boolean} Whether we know at compile-time that the browser is WEBKIT.45*/46goog.userAgent.ASSUME_WEBKIT =47goog.define('goog.userAgent.ASSUME_WEBKIT', false);484950/**51* @define {boolean} Whether we know at compile-time that the browser is a52* mobile device running WebKit e.g. iPhone or Android.53*/54goog.userAgent.ASSUME_MOBILE_WEBKIT =55goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false);565758/**59* @define {boolean} Whether we know at compile-time that the browser is OPERA,60* referring to Presto-based Opera.61*/62goog.userAgent.ASSUME_OPERA = goog.define('goog.userAgent.ASSUME_OPERA', false);636465/**66* @define {boolean} Whether the67* `goog.userAgent.isVersionOrHigher`68* function will return true for any version.69*/70goog.userAgent.ASSUME_ANY_VERSION =71goog.define('goog.userAgent.ASSUME_ANY_VERSION', false);727374/**75* Whether we know the browser engine at compile-time.76* @type {boolean}77* @private78*/79goog.userAgent.BROWSER_KNOWN_ = goog.userAgent.ASSUME_IE ||80goog.userAgent.ASSUME_EDGE || goog.userAgent.ASSUME_GECKO ||81goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.ASSUME_WEBKIT ||82goog.userAgent.ASSUME_OPERA;838485/**86* Returns the userAgent string for the current browser.87*88* @return {string} The userAgent string.89*/90goog.userAgent.getUserAgentString = function() {91'use strict';92return goog.labs.userAgent.util.getUserAgent();93};949596/**97* @return {?Navigator} The native navigator object.98*/99goog.userAgent.getNavigatorTyped = function() {100'use strict';101// Need a local navigator reference instead of using the global one,102// to avoid the rare case where they reference different objects.103// (in a WorkerPool, for example).104return goog.global['navigator'] || null;105};106107108/**109* TODO(nnaze): Change type to "Navigator" and update compilation targets.110* @return {?Object} The native navigator object.111*/112goog.userAgent.getNavigator = function() {113'use strict';114return goog.userAgent.getNavigatorTyped();115};116117118/**119* Whether the user agent is Presto-based Opera.120* @type {boolean}121*/122goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ?123goog.userAgent.ASSUME_OPERA :124goog.labs.userAgent.browser.isOpera();125126127/**128* Whether the user agent is Internet Explorer.129* @type {boolean}130*/131goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ?132goog.userAgent.ASSUME_IE :133goog.labs.userAgent.browser.isIE();134135136/**137* Whether the user agent is Microsoft Edge (EdgeHTML based).138* @type {boolean}139*/140goog.userAgent.EDGE = goog.userAgent.BROWSER_KNOWN_ ?141goog.userAgent.ASSUME_EDGE :142goog.labs.userAgent.engine.isEdge();143144145/**146* Whether the user agent is MS Internet Explorer or MS Edge (EdgeHTML based).147* @type {boolean}148*/149goog.userAgent.EDGE_OR_IE = goog.userAgent.EDGE || goog.userAgent.IE;150151152/**153* Whether the user agent is Gecko. Gecko is the rendering engine used by154* Mozilla, Firefox, and others.155* @type {boolean}156*/157goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ?158goog.userAgent.ASSUME_GECKO :159goog.labs.userAgent.engine.isGecko();160161162/**163* Whether the user agent is WebKit. WebKit is the rendering engine that164* Safari, Edge Chromium, Opera Chromium, Android and others use.165* @type {boolean}166*/167goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ?168goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT :169goog.labs.userAgent.engine.isWebKit();170171172/**173* Whether the user agent is running on a mobile device.174*175* This is a separate function so that the logic can be tested.176*177* TODO(nnaze): Investigate swapping in goog.labs.userAgent.device.isMobile().178*179* @return {boolean} Whether the user agent is running on a mobile device.180* @private181*/182goog.userAgent.isMobile_ = function() {183'use strict';184return goog.userAgent.WEBKIT &&185goog.labs.userAgent.util.matchUserAgent('Mobile');186};187188189/**190* Whether the user agent is running on a mobile device.191*192* TODO(nnaze): Consider deprecating MOBILE when labs.userAgent193* is promoted as the gecko/webkit logic is likely inaccurate.194*195* @type {boolean}196*/197goog.userAgent.MOBILE =198goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.isMobile_();199200201/**202* Used while transitioning code to use WEBKIT instead.203* @type {boolean}204* @deprecated Use {@link goog.userAgent.product.SAFARI} instead.205* TODO(nicksantos): Delete this from goog.userAgent.206*/207goog.userAgent.SAFARI = goog.userAgent.WEBKIT;208209210/**211* @return {string} the platform (operating system) the user agent is running212* on. Default to empty string because navigator.platform may not be defined213* (on Rhino, for example).214* @private215*/216goog.userAgent.determinePlatform_ = function() {217'use strict';218var navigator = goog.userAgent.getNavigatorTyped();219return navigator && navigator.platform || '';220};221222223/**224* The platform (operating system) the user agent is running on. Default to225* empty string because navigator.platform may not be defined (on Rhino, for226* example).227* @type {string}228*/229goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();230231232/**233* @define {boolean} Whether the user agent is running on a Macintosh operating234* system.235*/236goog.userAgent.ASSUME_MAC = goog.define('goog.userAgent.ASSUME_MAC', false);237238239/**240* @define {boolean} Whether the user agent is running on a Windows operating241* system.242*/243goog.userAgent.ASSUME_WINDOWS =244goog.define('goog.userAgent.ASSUME_WINDOWS', false);245246247/**248* @define {boolean} Whether the user agent is running on a Linux operating249* system.250*/251goog.userAgent.ASSUME_LINUX = goog.define('goog.userAgent.ASSUME_LINUX', false);252253254/**255* @define {boolean} Whether the user agent is running on a X11 windowing256* system.257*/258goog.userAgent.ASSUME_X11 = goog.define('goog.userAgent.ASSUME_X11', false);259260261/**262* @define {boolean} Whether the user agent is running on Android.263*/264goog.userAgent.ASSUME_ANDROID =265goog.define('goog.userAgent.ASSUME_ANDROID', false);266267268/**269* @define {boolean} Whether the user agent is running on an iPhone.270*/271goog.userAgent.ASSUME_IPHONE =272goog.define('goog.userAgent.ASSUME_IPHONE', false);273274275/**276* @define {boolean} Whether the user agent is running on an iPad.277*/278goog.userAgent.ASSUME_IPAD = goog.define('goog.userAgent.ASSUME_IPAD', false);279280281/**282* @define {boolean} Whether the user agent is running on an iPod.283*/284goog.userAgent.ASSUME_IPOD = goog.define('goog.userAgent.ASSUME_IPOD', false);285286287/**288* @define {boolean} Whether the user agent is running on KaiOS.289*/290goog.userAgent.ASSUME_KAIOS = goog.define('goog.userAgent.ASSUME_KAIOS', false);291292293/**294* @type {boolean}295* @private296*/297goog.userAgent.PLATFORM_KNOWN_ = goog.userAgent.ASSUME_MAC ||298goog.userAgent.ASSUME_WINDOWS || goog.userAgent.ASSUME_LINUX ||299goog.userAgent.ASSUME_X11 || goog.userAgent.ASSUME_ANDROID ||300goog.userAgent.ASSUME_IPHONE || goog.userAgent.ASSUME_IPAD ||301goog.userAgent.ASSUME_IPOD;302303304/**305* Whether the user agent is running on a Macintosh operating system.306* @type {boolean}307*/308goog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ?309goog.userAgent.ASSUME_MAC :310goog.labs.userAgent.platform.isMacintosh();311312313/**314* Whether the user agent is running on a Windows operating system.315* @type {boolean}316*/317goog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ?318goog.userAgent.ASSUME_WINDOWS :319goog.labs.userAgent.platform.isWindows();320321322/**323* Whether the user agent is Linux per the legacy behavior of324* goog.userAgent.LINUX, which considered ChromeOS to also be325* Linux.326* @return {boolean}327* @private328*/329goog.userAgent.isLegacyLinux_ = function() {330'use strict';331return goog.labs.userAgent.platform.isLinux() ||332goog.labs.userAgent.platform.isChromeOS();333};334335336/**337* Whether the user agent is running on a Linux operating system.338*339* Note that goog.userAgent.LINUX considers ChromeOS to be Linux,340* while goog.labs.userAgent.platform considers ChromeOS and341* Linux to be different OSes.342*343* @type {boolean}344*/345goog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ?346goog.userAgent.ASSUME_LINUX :347goog.userAgent.isLegacyLinux_();348349350/**351* @return {boolean} Whether the user agent is an X11 windowing system.352* @private353*/354goog.userAgent.isX11_ = function() {355'use strict';356var navigator = goog.userAgent.getNavigatorTyped();357return !!navigator &&358goog.string.internal.contains(navigator['appVersion'] || '', 'X11');359};360361362/**363* Whether the user agent is running on a X11 windowing system.364* @type {boolean}365*/366goog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ?367goog.userAgent.ASSUME_X11 :368goog.userAgent.isX11_();369370371/**372* Whether the user agent is running on Android.373* @type {boolean}374*/375goog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ?376goog.userAgent.ASSUME_ANDROID :377goog.labs.userAgent.platform.isAndroid();378379380/**381* Whether the user agent is running on an iPhone.382* @type {boolean}383*/384goog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ?385goog.userAgent.ASSUME_IPHONE :386goog.labs.userAgent.platform.isIphone();387388389/**390* Whether the user agent is running on an iPad.391* @type {boolean}392*/393goog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ?394goog.userAgent.ASSUME_IPAD :395goog.labs.userAgent.platform.isIpad();396397398/**399* Whether the user agent is running on an iPod.400* @type {boolean}401*/402goog.userAgent.IPOD = goog.userAgent.PLATFORM_KNOWN_ ?403goog.userAgent.ASSUME_IPOD :404goog.labs.userAgent.platform.isIpod();405406407/**408* Whether the user agent is running on iOS.409* @type {boolean}410*/411goog.userAgent.IOS = goog.userAgent.PLATFORM_KNOWN_ ?412(goog.userAgent.ASSUME_IPHONE || goog.userAgent.ASSUME_IPAD ||413goog.userAgent.ASSUME_IPOD) :414goog.labs.userAgent.platform.isIos();415416/**417* Whether the user agent is running on KaiOS.418* @type {boolean}419*/420goog.userAgent.KAIOS = goog.userAgent.PLATFORM_KNOWN_ ?421goog.userAgent.ASSUME_KAIOS :422goog.labs.userAgent.platform.isKaiOS();423424425/**426* @return {string} The string that describes the version number of the user427* agent.428* @private429*/430goog.userAgent.determineVersion_ = function() {431'use strict';432// All browsers have different ways to detect the version and they all have433// different naming schemes.434// version is a string rather than a number because it may contain 'b', 'a',435// and so on.436var version = '';437var arr = goog.userAgent.getVersionRegexResult_();438if (arr) {439version = arr ? arr[1] : '';440}441442if (goog.userAgent.IE) {443// IE9 can be in document mode 9 but be reporting an inconsistent user agent444// version. If it is identifying as a version lower than 9 we take the445// documentMode as the version instead. IE8 has similar behavior.446// It is recommended to set the X-UA-Compatible header to ensure that IE9447// uses documentMode 9.448var docMode = goog.userAgent.getDocumentMode_();449if (docMode != null && docMode > parseFloat(version)) {450return String(docMode);451}452}453454return version;455};456457458/**459* @return {?IArrayLike<string>|undefined} The version regex matches from460* parsing the user461* agent string. These regex statements must be executed inline so they can462* be compiled out by the closure compiler with the rest of the useragent463* detection logic when ASSUME_* is specified.464* @private465*/466goog.userAgent.getVersionRegexResult_ = function() {467'use strict';468var userAgent = goog.userAgent.getUserAgentString();469if (goog.userAgent.GECKO) {470return /rv\:([^\);]+)(\)|;)/.exec(userAgent);471}472if (goog.userAgent.EDGE) {473return /Edge\/([\d\.]+)/.exec(userAgent);474}475if (goog.userAgent.IE) {476return /\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(userAgent);477}478if (goog.userAgent.WEBKIT) {479// WebKit/125.4480return /WebKit\/(\S+)/.exec(userAgent);481}482if (goog.userAgent.OPERA) {483// If none of the above browsers were detected but the browser is Opera, the484// only string that is of interest is 'Version/<number>'.485return /(?:Version)[ \/]?(\S+)/.exec(userAgent);486}487return undefined;488};489490491/**492* @return {number|undefined} Returns the document mode (for testing).493* @private494*/495goog.userAgent.getDocumentMode_ = function() {496'use strict';497// NOTE(user): goog.userAgent may be used in context where there is no DOM.498var doc = goog.global['document'];499return doc ? doc['documentMode'] : undefined;500};501502503/**504* The version of the user agent. This is a string because it might contain505* 'b' (as in beta) as well as multiple dots.506* @type {string}507*/508goog.userAgent.VERSION = goog.userAgent.determineVersion_();509510511/**512* Compares two version numbers.513*514* @param {string} v1 Version of first item.515* @param {string} v2 Version of second item.516*517* @return {number} 1 if first argument is higher518* 0 if arguments are equal519* -1 if second argument is higher.520* @deprecated Use goog.string.compareVersions.521*/522goog.userAgent.compare = function(v1, v2) {523'use strict';524return goog.string.internal.compareVersions(v1, v2);525};526527528/**529* Cache for {@link goog.userAgent.isVersionOrHigher}.530* Calls to compareVersions are surprisingly expensive and, as a browser's531* version number is unlikely to change during a session, we cache the results.532* @const533* @private534*/535goog.userAgent.isVersionOrHigherCache_ = {};536537538/**539* Whether the user agent version is higher or the same as the given version.540* NOTE: When checking the version numbers for Firefox and Safari, be sure to541* use the engine's version, not the browser's version number. For example,542* Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11.543* Opera and Internet Explorer versions match the product release number.<br>544* @see <a href="http://en.wikipedia.org/wiki/Safari_version_history">545* Webkit</a>546* @see <a href="http://en.wikipedia.org/wiki/Gecko_engine">Gecko</a>547*548* @param {string|number} version The version to check.549* @return {boolean} Whether the user agent version is higher or the same as550* the given version.551*/552goog.userAgent.isVersionOrHigher = function(version) {553'use strict';554return goog.userAgent.ASSUME_ANY_VERSION ||555goog.reflect.cache(556goog.userAgent.isVersionOrHigherCache_, version, function() {557'use strict';558return goog.string.internal.compareVersions(559goog.userAgent.VERSION, version) >= 0;560});561};562563564/**565* Whether the IE effective document mode is higher or the same as the given566* document mode version.567* NOTE: Only for IE, return false for another browser.568*569* @param {number} documentMode The document mode version to check.570* @return {boolean} Whether the IE effective document mode is higher or the571* same as the given version.572*/573goog.userAgent.isDocumentModeOrHigher = function(documentMode) {574'use strict';575return Number(goog.userAgent.DOCUMENT_MODE) >= documentMode;576};577578579/**580* Deprecated alias to `goog.userAgent.isDocumentModeOrHigher`.581* @param {number} version The version to check.582* @return {boolean} Whether the IE effective document mode is higher or the583* same as the given version.584* @deprecated Use goog.userAgent.isDocumentModeOrHigher().585*/586goog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher;587588589/**590* For IE version < 7, documentMode is undefined, so attempt to use the591* CSS1Compat property to see if we are in standards mode. If we are in592* standards mode, treat the browser version as the document mode. Otherwise,593* IE is emulating version 5.594*595* NOTE(user): Support for IE < 7 is long gone, so this is now simplified.596* It returns document.documentMode for IE and undefined for everything else.597*598* @type {number|undefined}599* @const600*/601goog.userAgent.DOCUMENT_MODE = (function() {602'use strict';603var doc = goog.global['document'];604if (!doc || !goog.userAgent.IE) return undefined;605// This must be an IE user agent.606var documentMode = goog.userAgent.getDocumentMode_();607if (documentMode) return documentMode;608// The user agent version string begins with the major version.609// Parse the major version and truncate anything following.610var ieVersion = parseInt(goog.userAgent.VERSION, 10);611return ieVersion || undefined;612})();613614615