browser.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. // Copyright 2013 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 Closure user agent detection (Browser).
  16. * @see <a href="http://www.useragentstring.com/">User agent strings</a>
  17. * For more information on rendering engine, platform, or device see the other
  18. * sub-namespaces in goog.labs.userAgent, goog.labs.userAgent.platform,
  19. * goog.labs.userAgent.device respectively.)
  20. *
  21. * @author martone@google.com (Andy Martone)
  22. */
  23. goog.provide('goog.labs.userAgent.browser');
  24. goog.require('goog.array');
  25. goog.require('goog.labs.userAgent.util');
  26. goog.require('goog.object');
  27. goog.require('goog.string');
  28. // TODO(nnaze): Refactor to remove excessive exclusion logic in matching
  29. // functions.
  30. /**
  31. * @return {boolean} Whether the user's browser is Opera. Note: Chromium
  32. * based Opera (Opera 15+) is detected as Chrome to avoid unnecessary
  33. * special casing.
  34. * @private
  35. */
  36. goog.labs.userAgent.browser.matchOpera_ = function() {
  37. return goog.labs.userAgent.util.matchUserAgent('Opera');
  38. };
  39. /**
  40. * @return {boolean} Whether the user's browser is IE.
  41. * @private
  42. */
  43. goog.labs.userAgent.browser.matchIE_ = function() {
  44. return goog.labs.userAgent.util.matchUserAgent('Trident') ||
  45. goog.labs.userAgent.util.matchUserAgent('MSIE');
  46. };
  47. /**
  48. * @return {boolean} Whether the user's browser is Edge.
  49. * @private
  50. */
  51. goog.labs.userAgent.browser.matchEdge_ = function() {
  52. return goog.labs.userAgent.util.matchUserAgent('Edge');
  53. };
  54. /**
  55. * @return {boolean} Whether the user's browser is Firefox.
  56. * @private
  57. */
  58. goog.labs.userAgent.browser.matchFirefox_ = function() {
  59. return goog.labs.userAgent.util.matchUserAgent('Firefox');
  60. };
  61. /**
  62. * @return {boolean} Whether the user's browser is Safari.
  63. * @private
  64. */
  65. goog.labs.userAgent.browser.matchSafari_ = function() {
  66. return goog.labs.userAgent.util.matchUserAgent('Safari') &&
  67. !(goog.labs.userAgent.browser.matchChrome_() ||
  68. goog.labs.userAgent.browser.matchCoast_() ||
  69. goog.labs.userAgent.browser.matchOpera_() ||
  70. goog.labs.userAgent.browser.matchEdge_() ||
  71. goog.labs.userAgent.browser.isSilk() ||
  72. goog.labs.userAgent.util.matchUserAgent('Android'));
  73. };
  74. /**
  75. * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based
  76. * iOS browser).
  77. * @private
  78. */
  79. goog.labs.userAgent.browser.matchCoast_ = function() {
  80. return goog.labs.userAgent.util.matchUserAgent('Coast');
  81. };
  82. /**
  83. * @return {boolean} Whether the user's browser is iOS Webview.
  84. * @private
  85. */
  86. goog.labs.userAgent.browser.matchIosWebview_ = function() {
  87. // iOS Webview does not show up as Chrome or Safari. Also check for Opera's
  88. // WebKit-based iOS browser, Coast.
  89. return (goog.labs.userAgent.util.matchUserAgent('iPad') ||
  90. goog.labs.userAgent.util.matchUserAgent('iPhone')) &&
  91. !goog.labs.userAgent.browser.matchSafari_() &&
  92. !goog.labs.userAgent.browser.matchChrome_() &&
  93. !goog.labs.userAgent.browser.matchCoast_() &&
  94. goog.labs.userAgent.util.matchUserAgent('AppleWebKit');
  95. };
  96. /**
  97. * @return {boolean} Whether the user's browser is Chrome.
  98. * @private
  99. */
  100. goog.labs.userAgent.browser.matchChrome_ = function() {
  101. return (goog.labs.userAgent.util.matchUserAgent('Chrome') ||
  102. goog.labs.userAgent.util.matchUserAgent('CriOS')) &&
  103. !goog.labs.userAgent.browser.matchEdge_();
  104. };
  105. /**
  106. * @return {boolean} Whether the user's browser is the Android browser.
  107. * @private
  108. */
  109. goog.labs.userAgent.browser.matchAndroidBrowser_ = function() {
  110. // Android can appear in the user agent string for Chrome on Android.
  111. // This is not the Android standalone browser if it does.
  112. return goog.labs.userAgent.util.matchUserAgent('Android') &&
  113. !(goog.labs.userAgent.browser.isChrome() ||
  114. goog.labs.userAgent.browser.isFirefox() ||
  115. goog.labs.userAgent.browser.isOpera() ||
  116. goog.labs.userAgent.browser.isSilk());
  117. };
  118. /**
  119. * @return {boolean} Whether the user's browser is Opera.
  120. */
  121. goog.labs.userAgent.browser.isOpera = goog.labs.userAgent.browser.matchOpera_;
  122. /**
  123. * @return {boolean} Whether the user's browser is IE.
  124. */
  125. goog.labs.userAgent.browser.isIE = goog.labs.userAgent.browser.matchIE_;
  126. /**
  127. * @return {boolean} Whether the user's browser is Edge.
  128. */
  129. goog.labs.userAgent.browser.isEdge = goog.labs.userAgent.browser.matchEdge_;
  130. /**
  131. * @return {boolean} Whether the user's browser is Firefox.
  132. */
  133. goog.labs.userAgent.browser.isFirefox =
  134. goog.labs.userAgent.browser.matchFirefox_;
  135. /**
  136. * @return {boolean} Whether the user's browser is Safari.
  137. */
  138. goog.labs.userAgent.browser.isSafari = goog.labs.userAgent.browser.matchSafari_;
  139. /**
  140. * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based
  141. * iOS browser).
  142. */
  143. goog.labs.userAgent.browser.isCoast = goog.labs.userAgent.browser.matchCoast_;
  144. /**
  145. * @return {boolean} Whether the user's browser is iOS Webview.
  146. */
  147. goog.labs.userAgent.browser.isIosWebview =
  148. goog.labs.userAgent.browser.matchIosWebview_;
  149. /**
  150. * @return {boolean} Whether the user's browser is Chrome.
  151. */
  152. goog.labs.userAgent.browser.isChrome = goog.labs.userAgent.browser.matchChrome_;
  153. /**
  154. * @return {boolean} Whether the user's browser is the Android browser.
  155. */
  156. goog.labs.userAgent.browser.isAndroidBrowser =
  157. goog.labs.userAgent.browser.matchAndroidBrowser_;
  158. /**
  159. * For more information, see:
  160. * http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html
  161. * @return {boolean} Whether the user's browser is Silk.
  162. */
  163. goog.labs.userAgent.browser.isSilk = function() {
  164. return goog.labs.userAgent.util.matchUserAgent('Silk');
  165. };
  166. /**
  167. * @return {string} The browser version or empty string if version cannot be
  168. * determined. Note that for Internet Explorer, this returns the version of
  169. * the browser, not the version of the rendering engine. (IE 8 in
  170. * compatibility mode will return 8.0 rather than 7.0. To determine the
  171. * rendering engine version, look at document.documentMode instead. See
  172. * http://msdn.microsoft.com/en-us/library/cc196988(v=vs.85).aspx for more
  173. * details.)
  174. */
  175. goog.labs.userAgent.browser.getVersion = function() {
  176. var userAgentString = goog.labs.userAgent.util.getUserAgent();
  177. // Special case IE since IE's version is inside the parenthesis and
  178. // without the '/'.
  179. if (goog.labs.userAgent.browser.isIE()) {
  180. return goog.labs.userAgent.browser.getIEVersion_(userAgentString);
  181. }
  182. var versionTuples =
  183. goog.labs.userAgent.util.extractVersionTuples(userAgentString);
  184. // Construct a map for easy lookup.
  185. var versionMap = {};
  186. goog.array.forEach(versionTuples, function(tuple) {
  187. // Note that the tuple is of length three, but we only care about the
  188. // first two.
  189. var key = tuple[0];
  190. var value = tuple[1];
  191. versionMap[key] = value;
  192. });
  193. var versionMapHasKey = goog.partial(goog.object.containsKey, versionMap);
  194. // Gives the value with the first key it finds, otherwise empty string.
  195. function lookUpValueWithKeys(keys) {
  196. var key = goog.array.find(keys, versionMapHasKey);
  197. return versionMap[key] || '';
  198. }
  199. // Check Opera before Chrome since Opera 15+ has "Chrome" in the string.
  200. // See
  201. // http://my.opera.com/ODIN/blog/2013/07/15/opera-user-agent-strings-opera-15-and-beyond
  202. if (goog.labs.userAgent.browser.isOpera()) {
  203. // Opera 10 has Version/10.0 but Opera/9.8, so look for "Version" first.
  204. // Opera uses 'OPR' for more recent UAs.
  205. return lookUpValueWithKeys(['Version', 'Opera']);
  206. }
  207. // Check Edge before Chrome since it has Chrome in the string.
  208. if (goog.labs.userAgent.browser.isEdge()) {
  209. return lookUpValueWithKeys(['Edge']);
  210. }
  211. if (goog.labs.userAgent.browser.isChrome()) {
  212. return lookUpValueWithKeys(['Chrome', 'CriOS']);
  213. }
  214. // Usually products browser versions are in the third tuple after "Mozilla"
  215. // and the engine.
  216. var tuple = versionTuples[2];
  217. return tuple && tuple[1] || '';
  218. };
  219. /**
  220. * @param {string|number} version The version to check.
  221. * @return {boolean} Whether the browser version is higher or the same as the
  222. * given version.
  223. */
  224. goog.labs.userAgent.browser.isVersionOrHigher = function(version) {
  225. return goog.string.compareVersions(
  226. goog.labs.userAgent.browser.getVersion(), version) >= 0;
  227. };
  228. /**
  229. * Determines IE version. More information:
  230. * http://msdn.microsoft.com/en-us/library/ie/bg182625(v=vs.85).aspx#uaString
  231. * http://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
  232. * http://blogs.msdn.com/b/ie/archive/2010/03/23/introducing-ie9-s-user-agent-string.aspx
  233. * http://blogs.msdn.com/b/ie/archive/2009/01/09/the-internet-explorer-8-user-agent-string-updated-edition.aspx
  234. *
  235. * @param {string} userAgent the User-Agent.
  236. * @return {string}
  237. * @private
  238. */
  239. goog.labs.userAgent.browser.getIEVersion_ = function(userAgent) {
  240. // IE11 may identify itself as MSIE 9.0 or MSIE 10.0 due to an IE 11 upgrade
  241. // bug. Example UA:
  242. // Mozilla/5.0 (MSIE 9.0; Windows NT 6.1; WOW64; Trident/7.0; rv:11.0)
  243. // like Gecko.
  244. // See http://www.whatismybrowser.com/developers/unknown-user-agent-fragments.
  245. var rv = /rv: *([\d\.]*)/.exec(userAgent);
  246. if (rv && rv[1]) {
  247. return rv[1];
  248. }
  249. var version = '';
  250. var msie = /MSIE +([\d\.]+)/.exec(userAgent);
  251. if (msie && msie[1]) {
  252. // IE in compatibility mode usually identifies itself as MSIE 7.0; in this
  253. // case, use the Trident version to determine the version of IE. For more
  254. // details, see the links above.
  255. var tridentVersion = /Trident\/(\d.\d)/.exec(userAgent);
  256. if (msie[1] == '7.0') {
  257. if (tridentVersion && tridentVersion[1]) {
  258. switch (tridentVersion[1]) {
  259. case '4.0':
  260. version = '8.0';
  261. break;
  262. case '5.0':
  263. version = '9.0';
  264. break;
  265. case '6.0':
  266. version = '10.0';
  267. break;
  268. case '7.0':
  269. version = '11.0';
  270. break;
  271. }
  272. } else {
  273. version = '7.0';
  274. }
  275. } else {
  276. version = msie[1];
  277. }
  278. }
  279. return version;
  280. };