packages/fxa-content-server/app/scripts/lib/user-agent.js (114 lines of code) (raw):

/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import _ from 'underscore'; import UAParser from 'ua-parser-js'; const UserAgent = function (userAgent) { const uap = UAParser(userAgent); _.extend(uap, { /** * Check if the OS is Android. * * @returns {Boolean} */ isAndroid() { return this.os.name === 'Android'; }, /** * Check if the OS is iOS. * * @returns {Boolean} */ isIos() { return this.os.name === 'iOS' || this.isDesktopFirefoxOnIpad(); }, /** * Check if the device is iOS or Android. * * This does not work for latest iPad, see note above isDesktopFirefoxOnIpad. * * @returns {Boolean} */ isMobile() { return this.isIos() || this.isAndroid(); }, /** * iPads using FF iOS 13+ send a desktop UA. * The OS shows as a Mac, but 'Firefox iOS' in the UA family. * * NOTE, as of at least Sept 2024, this is no longer reliable. * The UA for iPad Safari exactly matches the UA for iPad Firefox * except for "Version" which is unreliable. * * @returns {Boolean} */ isDesktopFirefoxOnIpad() { return /Mac/.test(this.os.name) && /FxiOS/.test(this.ua); }, /** * Check if the browser is Mobile Safari. * * @returns {Boolean} */ isMobileSafari() { return this.browser.name === 'Mobile Safari'; }, /** * Check if the browser is Firefox * * @returns {Boolean} */ isFirefox() { return this.browser.name === 'Firefox'; }, /** * Check if the browser is Chrome * * @returns {Boolean} */ isChrome() { return this.browser.name === 'Chrome'; }, /** * Check if the browser is Chrome for Android * * @returns {Boolean} */ isChromeAndroid() { return this.isChrome() && this.isAndroid(); }, /** * Check if the browser is Edge * * @returns {Boolean} */ isEdge() { return this.browser.name === 'Edge'; }, /** * Check if the browser is Internet Explorer * * @returns {Boolean} */ isIE() { return this.browser.name === 'IE'; }, /** * Check if the browser is Firefox for Android * * @returns {Boolean} */ isFirefoxAndroid() { return this.isFirefox() && this.isAndroid(); }, /** * Check if the browser is Firefox for iOS * * @returns {Boolean} */ isFirefoxIos() { return this.isFirefox() && this.isIos(); }, /** * Check if the browser is Firefox desktop * * @returns {Boolean} */ isFirefoxDesktop() { return ( this.isFirefox() && !this.isFirefoxIos() && !this.isFirefoxAndroid() ); }, /** * Check if the browser is Thunderbird desktop. Note that parseVersion will * return the wrong value as the ua-parser-js doesn't detect Thunderbird. * * @returns {Boolean} */ isThunderbirdDesktop() { return /(Thunderbird)\/([\w\.]+)/.test(this.ua); }, /** * Some browsers do not support SVG Transform Origin and * some of our SVGs will not animate properly. * * We browser detect and determine the version of the SVG to show * based on this function * @returns {boolean} */ supportsSvgTransformOrigin() { // everything except Safari iOS / Edge / IE support TransformOrigin // Ref: https://developer.mozilla.org/docs/Web/CSS/transform-origin return !(this.isIos() || this.isEdge() || this.isIE()); }, /** * Parse uap.browser.version into an object with * `major`, `minor`, and `patch` * * @returns {Object} */ parseVersion() { // this.browser.version can be `undefined` const browserVersion = this.browser.version ? this.browser.version.split('.') : []; return { major: parseInt(browserVersion[0] || 0, 10), minor: parseInt(browserVersion[1] || 0, 10), patch: parseInt(browserVersion[2] || 0, 10), }; }, /** * Parse uap.os.version into an object with * `major`, `minor`, and `patch` * * @returns {Object} */ parseOsVersion() { // this.os.version can be `undefined` const osVersion = this.os.version ? this.os.version.split('.') : []; return { major: parseInt(osVersion[0] || 0, 10), minor: parseInt(osVersion[1] || 0, 10), patch: parseInt(osVersion[2] || 0, 10), }; }, /** * Get the generic operating system name. * * @param {String} [os=this.os.name] Full Operating System name * @returns {String} generic operating system name */ genericOSName(os = this.os.name) { return UserAgent.toGenericOSName(os); }, /** * Get the generic device type, one of `mobile`, `tablet`, or `desktop`. * * @param {String} [type=this.device.type] Full device type * @returns {String} generic device type. */ genericDeviceType(type = this.device.type) { switch (type) { case 'mobile': case 'tablet': return type; case 'smarttv': case 'wearable': case 'embedded': return 'mobile'; default: return 'desktop'; } }, }); return uap; }; /** * Simplifies user agent operating system names (50+) to generic popular names (~6) * * @param {String} os Operating System name from * @returns {String} generic operating system name */ UserAgent.toGenericOSName = function toGenericOSName(os) { if (/^Windows/.test(os)) { return 'Windows'; } if (/^Android/.test(os)) { return 'Android'; } if (/^Mac OS/.test(os)) { return 'macOS'; } if (/^iOS/.test(os)) { return 'iOS'; } if ( /^Ubuntu/.test(os) || /^Linux/.test(os) || /^Fedora/.test(os) || /^Red Hat/.test(os) || /^Debian/.test(os) ) { return 'Linux'; } return 'Unknown'; }; export default UserAgent;