lib/goog/useragent/useragent.js

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