useragent.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. // Copyright 2006 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Rendering engine detection.
  16. * @see <a href="http://www.useragentstring.com/">User agent strings</a>
  17. * For information on the browser brand (such as Safari versus Chrome), see
  18. * goog.userAgent.product.
  19. * @author arv@google.com (Erik Arvidsson)
  20. * @see ../demos/useragent.html
  21. */
  22. goog.provide('goog.userAgent');
  23. goog.require('goog.labs.userAgent.browser');
  24. goog.require('goog.labs.userAgent.engine');
  25. goog.require('goog.labs.userAgent.platform');
  26. goog.require('goog.labs.userAgent.util');
  27. goog.require('goog.reflect');
  28. goog.require('goog.string');
  29. /**
  30. * @define {boolean} Whether we know at compile-time that the browser is IE.
  31. */
  32. goog.define('goog.userAgent.ASSUME_IE', false);
  33. /**
  34. * @define {boolean} Whether we know at compile-time that the browser is EDGE.
  35. */
  36. goog.define('goog.userAgent.ASSUME_EDGE', false);
  37. /**
  38. * @define {boolean} Whether we know at compile-time that the browser is GECKO.
  39. */
  40. goog.define('goog.userAgent.ASSUME_GECKO', false);
  41. /**
  42. * @define {boolean} Whether we know at compile-time that the browser is WEBKIT.
  43. */
  44. goog.define('goog.userAgent.ASSUME_WEBKIT', false);
  45. /**
  46. * @define {boolean} Whether we know at compile-time that the browser is a
  47. * mobile device running WebKit e.g. iPhone or Android.
  48. */
  49. goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false);
  50. /**
  51. * @define {boolean} Whether we know at compile-time that the browser is OPERA.
  52. */
  53. goog.define('goog.userAgent.ASSUME_OPERA', false);
  54. /**
  55. * @define {boolean} Whether the
  56. * {@code goog.userAgent.isVersionOrHigher}
  57. * function will return true for any version.
  58. */
  59. goog.define('goog.userAgent.ASSUME_ANY_VERSION', false);
  60. /**
  61. * Whether we know the browser engine at compile-time.
  62. * @type {boolean}
  63. * @private
  64. */
  65. goog.userAgent.BROWSER_KNOWN_ = goog.userAgent.ASSUME_IE ||
  66. goog.userAgent.ASSUME_EDGE || goog.userAgent.ASSUME_GECKO ||
  67. goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.ASSUME_WEBKIT ||
  68. goog.userAgent.ASSUME_OPERA;
  69. /**
  70. * Returns the userAgent string for the current browser.
  71. *
  72. * @return {string} The userAgent string.
  73. */
  74. goog.userAgent.getUserAgentString = function() {
  75. return goog.labs.userAgent.util.getUserAgent();
  76. };
  77. /**
  78. * TODO(nnaze): Change type to "Navigator" and update compilation targets.
  79. * @return {?Object} The native navigator object.
  80. */
  81. goog.userAgent.getNavigator = function() {
  82. // Need a local navigator reference instead of using the global one,
  83. // to avoid the rare case where they reference different objects.
  84. // (in a WorkerPool, for example).
  85. return goog.global['navigator'] || null;
  86. };
  87. /**
  88. * Whether the user agent is Opera.
  89. * @type {boolean}
  90. */
  91. goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ?
  92. goog.userAgent.ASSUME_OPERA :
  93. goog.labs.userAgent.browser.isOpera();
  94. /**
  95. * Whether the user agent is Internet Explorer.
  96. * @type {boolean}
  97. */
  98. goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ?
  99. goog.userAgent.ASSUME_IE :
  100. goog.labs.userAgent.browser.isIE();
  101. /**
  102. * Whether the user agent is Microsoft Edge.
  103. * @type {boolean}
  104. */
  105. goog.userAgent.EDGE = goog.userAgent.BROWSER_KNOWN_ ?
  106. goog.userAgent.ASSUME_EDGE :
  107. goog.labs.userAgent.engine.isEdge();
  108. /**
  109. * Whether the user agent is MS Internet Explorer or MS Edge.
  110. * @type {boolean}
  111. */
  112. goog.userAgent.EDGE_OR_IE = goog.userAgent.EDGE || goog.userAgent.IE;
  113. /**
  114. * Whether the user agent is Gecko. Gecko is the rendering engine used by
  115. * Mozilla, Firefox, and others.
  116. * @type {boolean}
  117. */
  118. goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ?
  119. goog.userAgent.ASSUME_GECKO :
  120. goog.labs.userAgent.engine.isGecko();
  121. /**
  122. * Whether the user agent is WebKit. WebKit is the rendering engine that
  123. * Safari, Android and others use.
  124. * @type {boolean}
  125. */
  126. goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ?
  127. goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT :
  128. goog.labs.userAgent.engine.isWebKit();
  129. /**
  130. * Whether the user agent is running on a mobile device.
  131. *
  132. * This is a separate function so that the logic can be tested.
  133. *
  134. * TODO(nnaze): Investigate swapping in goog.labs.userAgent.device.isMobile().
  135. *
  136. * @return {boolean} Whether the user agent is running on a mobile device.
  137. * @private
  138. */
  139. goog.userAgent.isMobile_ = function() {
  140. return goog.userAgent.WEBKIT &&
  141. goog.labs.userAgent.util.matchUserAgent('Mobile');
  142. };
  143. /**
  144. * Whether the user agent is running on a mobile device.
  145. *
  146. * TODO(nnaze): Consider deprecating MOBILE when labs.userAgent
  147. * is promoted as the gecko/webkit logic is likely inaccurate.
  148. *
  149. * @type {boolean}
  150. */
  151. goog.userAgent.MOBILE =
  152. goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.isMobile_();
  153. /**
  154. * Used while transitioning code to use WEBKIT instead.
  155. * @type {boolean}
  156. * @deprecated Use {@link goog.userAgent.product.SAFARI} instead.
  157. * TODO(nicksantos): Delete this from goog.userAgent.
  158. */
  159. goog.userAgent.SAFARI = goog.userAgent.WEBKIT;
  160. /**
  161. * @return {string} the platform (operating system) the user agent is running
  162. * on. Default to empty string because navigator.platform may not be defined
  163. * (on Rhino, for example).
  164. * @private
  165. */
  166. goog.userAgent.determinePlatform_ = function() {
  167. var navigator = goog.userAgent.getNavigator();
  168. return navigator && navigator.platform || '';
  169. };
  170. /**
  171. * The platform (operating system) the user agent is running on. Default to
  172. * empty string because navigator.platform may not be defined (on Rhino, for
  173. * example).
  174. * @type {string}
  175. */
  176. goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();
  177. /**
  178. * @define {boolean} Whether the user agent is running on a Macintosh operating
  179. * system.
  180. */
  181. goog.define('goog.userAgent.ASSUME_MAC', false);
  182. /**
  183. * @define {boolean} Whether the user agent is running on a Windows operating
  184. * system.
  185. */
  186. goog.define('goog.userAgent.ASSUME_WINDOWS', false);
  187. /**
  188. * @define {boolean} Whether the user agent is running on a Linux operating
  189. * system.
  190. */
  191. goog.define('goog.userAgent.ASSUME_LINUX', false);
  192. /**
  193. * @define {boolean} Whether the user agent is running on a X11 windowing
  194. * system.
  195. */
  196. goog.define('goog.userAgent.ASSUME_X11', false);
  197. /**
  198. * @define {boolean} Whether the user agent is running on Android.
  199. */
  200. goog.define('goog.userAgent.ASSUME_ANDROID', false);
  201. /**
  202. * @define {boolean} Whether the user agent is running on an iPhone.
  203. */
  204. goog.define('goog.userAgent.ASSUME_IPHONE', false);
  205. /**
  206. * @define {boolean} Whether the user agent is running on an iPad.
  207. */
  208. goog.define('goog.userAgent.ASSUME_IPAD', false);
  209. /**
  210. * @define {boolean} Whether the user agent is running on an iPod.
  211. */
  212. goog.define('goog.userAgent.ASSUME_IPOD', false);
  213. /**
  214. * @type {boolean}
  215. * @private
  216. */
  217. goog.userAgent.PLATFORM_KNOWN_ = goog.userAgent.ASSUME_MAC ||
  218. goog.userAgent.ASSUME_WINDOWS || goog.userAgent.ASSUME_LINUX ||
  219. goog.userAgent.ASSUME_X11 || goog.userAgent.ASSUME_ANDROID ||
  220. goog.userAgent.ASSUME_IPHONE || goog.userAgent.ASSUME_IPAD ||
  221. goog.userAgent.ASSUME_IPOD;
  222. /**
  223. * Whether the user agent is running on a Macintosh operating system.
  224. * @type {boolean}
  225. */
  226. goog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ?
  227. goog.userAgent.ASSUME_MAC :
  228. goog.labs.userAgent.platform.isMacintosh();
  229. /**
  230. * Whether the user agent is running on a Windows operating system.
  231. * @type {boolean}
  232. */
  233. goog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ?
  234. goog.userAgent.ASSUME_WINDOWS :
  235. goog.labs.userAgent.platform.isWindows();
  236. /**
  237. * Whether the user agent is Linux per the legacy behavior of
  238. * goog.userAgent.LINUX, which considered ChromeOS to also be
  239. * Linux.
  240. * @return {boolean}
  241. * @private
  242. */
  243. goog.userAgent.isLegacyLinux_ = function() {
  244. return goog.labs.userAgent.platform.isLinux() ||
  245. goog.labs.userAgent.platform.isChromeOS();
  246. };
  247. /**
  248. * Whether the user agent is running on a Linux operating system.
  249. *
  250. * Note that goog.userAgent.LINUX considers ChromeOS to be Linux,
  251. * while goog.labs.userAgent.platform considers ChromeOS and
  252. * Linux to be different OSes.
  253. *
  254. * @type {boolean}
  255. */
  256. goog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ?
  257. goog.userAgent.ASSUME_LINUX :
  258. goog.userAgent.isLegacyLinux_();
  259. /**
  260. * @return {boolean} Whether the user agent is an X11 windowing system.
  261. * @private
  262. */
  263. goog.userAgent.isX11_ = function() {
  264. var navigator = goog.userAgent.getNavigator();
  265. return !!navigator &&
  266. goog.string.contains(navigator['appVersion'] || '', 'X11');
  267. };
  268. /**
  269. * Whether the user agent is running on a X11 windowing system.
  270. * @type {boolean}
  271. */
  272. goog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ?
  273. goog.userAgent.ASSUME_X11 :
  274. goog.userAgent.isX11_();
  275. /**
  276. * Whether the user agent is running on Android.
  277. * @type {boolean}
  278. */
  279. goog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ?
  280. goog.userAgent.ASSUME_ANDROID :
  281. goog.labs.userAgent.platform.isAndroid();
  282. /**
  283. * Whether the user agent is running on an iPhone.
  284. * @type {boolean}
  285. */
  286. goog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ?
  287. goog.userAgent.ASSUME_IPHONE :
  288. goog.labs.userAgent.platform.isIphone();
  289. /**
  290. * Whether the user agent is running on an iPad.
  291. * @type {boolean}
  292. */
  293. goog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ?
  294. goog.userAgent.ASSUME_IPAD :
  295. goog.labs.userAgent.platform.isIpad();
  296. /**
  297. * Whether the user agent is running on an iPod.
  298. * @type {boolean}
  299. */
  300. goog.userAgent.IPOD = goog.userAgent.PLATFORM_KNOWN_ ?
  301. goog.userAgent.ASSUME_IPOD :
  302. goog.labs.userAgent.platform.isIpod();
  303. /**
  304. * Whether the user agent is running on iOS.
  305. * @type {boolean}
  306. */
  307. goog.userAgent.IOS = goog.userAgent.PLATFORM_KNOWN_ ?
  308. (goog.userAgent.ASSUME_IPHONE || goog.userAgent.ASSUME_IPAD ||
  309. goog.userAgent.ASSUME_IPOD) :
  310. goog.labs.userAgent.platform.isIos();
  311. /**
  312. * @return {string} The string that describes the version number of the user
  313. * agent.
  314. * @private
  315. */
  316. goog.userAgent.determineVersion_ = function() {
  317. // All browsers have different ways to detect the version and they all have
  318. // different naming schemes.
  319. // version is a string rather than a number because it may contain 'b', 'a',
  320. // and so on.
  321. var version = '';
  322. var arr = goog.userAgent.getVersionRegexResult_();
  323. if (arr) {
  324. version = arr ? arr[1] : '';
  325. }
  326. if (goog.userAgent.IE) {
  327. // IE9 can be in document mode 9 but be reporting an inconsistent user agent
  328. // version. If it is identifying as a version lower than 9 we take the
  329. // documentMode as the version instead. IE8 has similar behavior.
  330. // It is recommended to set the X-UA-Compatible header to ensure that IE9
  331. // uses documentMode 9.
  332. var docMode = goog.userAgent.getDocumentMode_();
  333. if (docMode != null && docMode > parseFloat(version)) {
  334. return String(docMode);
  335. }
  336. }
  337. return version;
  338. };
  339. /**
  340. * @return {?Array|undefined} The version regex matches from parsing the user
  341. * agent string. These regex statements must be executed inline so they can
  342. * be compiled out by the closure compiler with the rest of the useragent
  343. * detection logic when ASSUME_* is specified.
  344. * @private
  345. */
  346. goog.userAgent.getVersionRegexResult_ = function() {
  347. var userAgent = goog.userAgent.getUserAgentString();
  348. if (goog.userAgent.GECKO) {
  349. return /rv\:([^\);]+)(\)|;)/.exec(userAgent);
  350. }
  351. if (goog.userAgent.EDGE) {
  352. return /Edge\/([\d\.]+)/.exec(userAgent);
  353. }
  354. if (goog.userAgent.IE) {
  355. return /\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(userAgent);
  356. }
  357. if (goog.userAgent.WEBKIT) {
  358. // WebKit/125.4
  359. return /WebKit\/(\S+)/.exec(userAgent);
  360. }
  361. if (goog.userAgent.OPERA) {
  362. // If none of the above browsers were detected but the browser is Opera, the
  363. // only string that is of interest is 'Version/<number>'.
  364. return /(?:Version)[ \/]?(\S+)/.exec(userAgent);
  365. }
  366. return undefined;
  367. };
  368. /**
  369. * @return {number|undefined} Returns the document mode (for testing).
  370. * @private
  371. */
  372. goog.userAgent.getDocumentMode_ = function() {
  373. // NOTE(user): goog.userAgent may be used in context where there is no DOM.
  374. var doc = goog.global['document'];
  375. return doc ? doc['documentMode'] : undefined;
  376. };
  377. /**
  378. * The version of the user agent. This is a string because it might contain
  379. * 'b' (as in beta) as well as multiple dots.
  380. * @type {string}
  381. */
  382. goog.userAgent.VERSION = goog.userAgent.determineVersion_();
  383. /**
  384. * Compares two version numbers.
  385. *
  386. * @param {string} v1 Version of first item.
  387. * @param {string} v2 Version of second item.
  388. *
  389. * @return {number} 1 if first argument is higher
  390. * 0 if arguments are equal
  391. * -1 if second argument is higher.
  392. * @deprecated Use goog.string.compareVersions.
  393. */
  394. goog.userAgent.compare = function(v1, v2) {
  395. return goog.string.compareVersions(v1, v2);
  396. };
  397. /**
  398. * Cache for {@link goog.userAgent.isVersionOrHigher}.
  399. * Calls to compareVersions are surprisingly expensive and, as a browser's
  400. * version number is unlikely to change during a session, we cache the results.
  401. * @const
  402. * @private
  403. */
  404. goog.userAgent.isVersionOrHigherCache_ = {};
  405. /**
  406. * Whether the user agent version is higher or the same as the given version.
  407. * NOTE: When checking the version numbers for Firefox and Safari, be sure to
  408. * use the engine's version, not the browser's version number. For example,
  409. * Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11.
  410. * Opera and Internet Explorer versions match the product release number.<br>
  411. * @see <a href="http://en.wikipedia.org/wiki/Safari_version_history">
  412. * Webkit</a>
  413. * @see <a href="http://en.wikipedia.org/wiki/Gecko_engine">Gecko</a>
  414. *
  415. * @param {string|number} version The version to check.
  416. * @return {boolean} Whether the user agent version is higher or the same as
  417. * the given version.
  418. */
  419. goog.userAgent.isVersionOrHigher = function(version) {
  420. return goog.userAgent.ASSUME_ANY_VERSION ||
  421. goog.reflect.cache(
  422. goog.userAgent.isVersionOrHigherCache_, version, function() {
  423. return goog.string.compareVersions(
  424. goog.userAgent.VERSION, version) >= 0;
  425. });
  426. };
  427. /**
  428. * Deprecated alias to {@code goog.userAgent.isVersionOrHigher}.
  429. * @param {string|number} version The version to check.
  430. * @return {boolean} Whether the user agent version is higher or the same as
  431. * the given version.
  432. * @deprecated Use goog.userAgent.isVersionOrHigher().
  433. */
  434. goog.userAgent.isVersion = goog.userAgent.isVersionOrHigher;
  435. /**
  436. * Whether the IE effective document mode is higher or the same as the given
  437. * document mode version.
  438. * NOTE: Only for IE, return false for another browser.
  439. *
  440. * @param {number} documentMode The document mode version to check.
  441. * @return {boolean} Whether the IE effective document mode is higher or the
  442. * same as the given version.
  443. */
  444. goog.userAgent.isDocumentModeOrHigher = function(documentMode) {
  445. return Number(goog.userAgent.DOCUMENT_MODE) >= documentMode;
  446. };
  447. /**
  448. * Deprecated alias to {@code goog.userAgent.isDocumentModeOrHigher}.
  449. * @param {number} version The version to check.
  450. * @return {boolean} Whether the IE effective document mode is higher or the
  451. * same as the given version.
  452. * @deprecated Use goog.userAgent.isDocumentModeOrHigher().
  453. */
  454. goog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher;
  455. /**
  456. * For IE version < 7, documentMode is undefined, so attempt to use the
  457. * CSS1Compat property to see if we are in standards mode. If we are in
  458. * standards mode, treat the browser version as the document mode. Otherwise,
  459. * IE is emulating version 5.
  460. * @type {number|undefined}
  461. * @const
  462. */
  463. goog.userAgent.DOCUMENT_MODE = (function() {
  464. var doc = goog.global['document'];
  465. var mode = goog.userAgent.getDocumentMode_();
  466. if (!doc || !goog.userAgent.IE) {
  467. return undefined;
  468. }
  469. return mode || (doc['compatMode'] == 'CSS1Compat' ?
  470. parseInt(goog.userAgent.VERSION, 10) :
  471. 5);
  472. })();