- /*
- * Copyright 2000-2018 Vaadin Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
- package com.vaadin.shared;
-
- import java.io.Serializable;
- import java.util.Locale;
-
- /**
- * Class that parses the user agent string from the browser and provides
- * information about the browser. Used internally by
- * {@link com.vaadin.client.BrowserInfo} and
- * {@link com.vaadin.server.WebBrowser}. Should not be used directly.
- *
- * @author Vaadin Ltd.
- * @since 6.3
- */
- public class VBrowserDetails implements Serializable {
-
- private boolean isGecko = false;
- private boolean isWebKit = false;
- private boolean isPresto = false;
- private boolean isTrident = false;
-
- private boolean isChromeFrameCapable = false;
- private boolean isChromeFrame = false;
-
- private boolean isSafari = false;
- private boolean isChrome = false;
- private boolean isFirefox = false;
- private boolean isOpera = false;
- private boolean isIE = false;
- private boolean isEdge = false;
- private boolean isPhantomJS = false;
-
- private boolean isWindowsPhone;
- private boolean isIPad;
- private boolean isIPhone;
- private boolean isChromeOS;
-
- private OperatingSystem os = OperatingSystem.UNKNOWN;
-
- public enum OperatingSystem {
- UNKNOWN, WINDOWS, MACOSX, LINUX, IOS, ANDROID, CHROMEOS;
- }
-
- private float browserEngineVersion = -1;
- private int browserMajorVersion = -1;
- private int browserMinorVersion = -1;
- private String browserVersion;
-
- private int osMajorVersion = -1;
- private int osMinorVersion = -1;
-
- /**
- * Create an instance based on the given user agent.
- *
- * @param userAgent
- * User agent as provided by the browser.
- */
- public VBrowserDetails(String userAgent) {
- userAgent = userAgent.toLowerCase(Locale.ROOT);
-
- // browser engine name
- isGecko = userAgent.indexOf("gecko") != -1
- && userAgent.indexOf("webkit") == -1
- && userAgent.indexOf("trident/") == -1;
- isPresto = userAgent.indexOf(" presto/") != -1;
- isTrident = userAgent.indexOf("trident/") != -1;
- isWebKit = !isTrident && userAgent.indexOf("applewebkit") != -1;
-
- // browser name
- isChrome = userAgent.indexOf(" chrome/") != -1
- || userAgent.indexOf(" crios/") != -1;
- isOpera = userAgent.indexOf("opera") != -1;
- isIE = userAgent.indexOf("msie") != -1 && !isOpera
- && (userAgent.indexOf("webtv") == -1);
- // IE 11 no longer contains MSIE in the user agent
- isIE = isIE || isTrident;
-
- isPhantomJS = userAgent.indexOf("phantomjs/") != -1;
- isFirefox = userAgent.indexOf(" firefox/") != -1
- || userAgent.indexOf("fxios/") != -1;
- isSafari = !isChrome && !isIE && !isPhantomJS && !isFirefox
- && userAgent.indexOf("safari") != -1;
- if (userAgent.indexOf(" edge/") != -1) {
- isEdge = true;
- isChrome = false;
- isOpera = false;
- isIE = false;
- isSafari = false;
- isFirefox = false;
- isWebKit = false;
- isGecko = false;
- isPhantomJS = false;
- }
-
- // chromeframe
- isChromeFrameCapable = userAgent.indexOf("chromeframe") != -1;
- isChromeFrame = isChromeFrameCapable && !isChrome;
-
- // Rendering engine version
- try {
- if (isGecko) {
- int rvPos = userAgent.indexOf("rv:");
- if (rvPos >= 0) {
- String tmp = userAgent.substring(rvPos + 3);
- tmp = tmp.replaceFirst("(\\.[0-9]+).+", "$1");
- browserEngineVersion = Float.parseFloat(tmp);
- }
- } else if (isWebKit) {
- String tmp = userAgent
- .substring(userAgent.indexOf("webkit/") + 7);
- tmp = tmp.replaceFirst("([0-9]+)[^0-9].+", "$1");
- browserEngineVersion = Float.parseFloat(tmp);
- } else if (isTrident) {
- String tmp = userAgent
- .substring(userAgent.indexOf("trident/") + 8);
- tmp = tmp.replaceFirst("([0-9]+\\.[0-9]+).*", "$1");
- browserEngineVersion = Float.parseFloat(tmp);
- if (browserEngineVersion > 7) {
- browserEngineVersion = 7;
- }
- } else if (isEdge) {
- browserEngineVersion = 0;
- }
- } catch (Exception e) {
- // Browser engine version parsing failed
- System.err.println(
- "Browser engine version parsing failed for: " + userAgent);
- }
-
- // Browser version
- try {
- if (isIE) {
- if (userAgent.indexOf("msie") == -1) {
- // IE 11+
- int rvPos = userAgent.indexOf("rv:");
- if (rvPos >= 0) {
- int i = rvPos + "rv:".length();
- browserVersion = findBrowserVersion(userAgent, i);
- parseVersionString(browserVersion);
- }
- } else if (isTrident) {
- // See
- // https://msdn.microsoft.com/en-us/library/ms537503(v=vs.85).aspx#TriToken
- setIEMode((int) browserEngineVersion + 4);
- } else {
- int i = userAgent.indexOf("msie ") + 5;
- browserVersion = findBrowserVersion(userAgent, i);
- parseVersionString(browserVersion);
- }
- } else if (isFirefox) {
- int i = userAgent.indexOf(" firefox/");
- if (i != -1) {
- i += " firefox/".length();
- } else {
- i = userAgent.indexOf(" fxios/") + " fxios/".length();
- }
- browserVersion = findBrowserVersion(userAgent, i);
- parseVersionString(browserVersion);
- } else if (isChrome) {
- int i = userAgent.indexOf(" chrome/");
- if (i != -1) {
- i += " chrome/".length();
- } else {
- i = userAgent.indexOf(" crios/") + " crios/".length();
- }
- browserVersion = findBrowserVersion(userAgent, i);
- parseVersionString(browserVersion);
- } else if (isSafari) {
- int i = userAgent.indexOf(" version/") + 9;
- browserVersion = findBrowserVersion(userAgent, i);
- parseVersionString(browserVersion);
- } else if (isOpera) {
- int i = userAgent.indexOf(" version/");
- if (i != -1) {
- // Version present in Opera 10 and newer
- i += 9; // " version/".length
- } else {
- i = userAgent.indexOf("opera/") + 6;
- }
- browserVersion = findBrowserVersion(userAgent, i);
- parseVersionString(browserVersion);
- } else if (isEdge) {
- int i = userAgent.indexOf(" edge/") + 6;
- browserVersion = findBrowserVersion(userAgent, i);
- parseVersionString(browserVersion);
- } else if (isPhantomJS) {
- String prefix = " phantomjs/";
- int i = userAgent.indexOf(prefix) + prefix.length();
- browserVersion = findBrowserVersion(userAgent, i);
- parseVersionString(browserVersion);
- }
- } catch (Exception e) {
- // Browser version parsing failed
- System.err.println(
- "Browser version parsing failed for: " + userAgent);
- }
-
- // Operating system
- if (userAgent.contains("windows ")) {
- os = OperatingSystem.WINDOWS;
- isWindowsPhone = userAgent.contains("windows phone");
- } else if (userAgent.contains("android")) {
- os = OperatingSystem.ANDROID;
- parseAndroidVersion(userAgent);
- } else if (userAgent.contains("linux")) {
- os = OperatingSystem.LINUX;
- } else if (userAgent.contains("macintosh")
- || userAgent.contains("mac osx")
- || userAgent.contains("mac os x")) {
- isIPad = userAgent.contains("ipad");
- isIPhone = userAgent.contains("iphone");
- if (isIPad || userAgent.contains("ipod") || isIPhone) {
- os = OperatingSystem.IOS;
- parseIOSVersion(userAgent);
- } else {
- os = OperatingSystem.MACOSX;
- }
- } else if (userAgent.contains("; cros ")) {
- os = OperatingSystem.CHROMEOS;
- isChromeOS = true;
- parseChromeOSVersion(userAgent);
- }
- }
-
- // (X11; CrOS armv7l 6946.63.0)
- private void parseChromeOSVersion(String userAgent) {
- int start = userAgent.indexOf("; cros ");
- if (start == -1) {
- return;
- }
- int end = userAgent.indexOf(')', start);
- if (end == -1) {
- return;
- }
- int cur = end;
- while (cur >= start && userAgent.charAt(cur) != ' ') {
- cur--;
- }
- if (cur == start) {
- return;
- }
- String osVersionString = userAgent.substring(cur + 1, end);
- String[] parts = osVersionString.split("\\.");
- parseChromeOsVersion(parts);
- }
-
- private void parseChromeOsVersion(String[] parts) {
- osMajorVersion = -1;
- osMinorVersion = -1;
-
- if (parts.length > 2) {
- try {
- osMajorVersion = Integer.parseInt(parts[1]);
- } catch (Exception e) {
- }
- try {
- osMinorVersion = Integer.parseInt(parts[0]);
- } catch (Exception e) {
- }
- }
- }
-
- private void parseAndroidVersion(String userAgent) {
- // Android 5.1;
- if (!userAgent.contains("android")) {
- return;
- }
-
- String osVersionString = safeSubstring(userAgent,
- userAgent.indexOf("android ") + "android ".length(),
- userAgent.length());
- osVersionString = safeSubstring(osVersionString, 0,
- osVersionString.indexOf(";"));
- String[] parts = osVersionString.split("\\.");
- parseOsVersion(parts);
- }
-
- private void parseIOSVersion(String userAgent) {
- // OS 5_1 like Mac OS X
- if (!userAgent.contains("os ") || !userAgent.contains(" like mac")) {
- return;
- }
-
- String osVersionString = safeSubstring(userAgent,
- userAgent.indexOf("os ") + 3, userAgent.indexOf(" like mac"));
- String[] parts = osVersionString.split("_");
- parseOsVersion(parts);
- }
-
- private void parseOsVersion(String[] parts) {
- osMajorVersion = -1;
- osMinorVersion = -1;
-
- if (parts.length >= 1) {
- try {
- osMajorVersion = Integer.parseInt(parts[0]);
- } catch (Exception e) {
- }
- }
- if (parts.length >= 2) {
- try {
- osMinorVersion = Integer.parseInt(parts[1]);
- } catch (Exception e) {
- }
- // Some Androids report version numbers as "2.1-update1"
- if (osMinorVersion == -1 && parts[1].contains("-")) {
- try {
- osMinorVersion = Integer.parseInt(
- parts[1].substring(0, parts[1].indexOf('-')));
- } catch (Exception ee) {
- }
- }
- }
-
- }
-
- private void parseVersionString(String versionString) {
- int idx = versionString.indexOf('.');
- if (idx < 0) {
- idx = versionString.length();
- }
- browserMajorVersion = Integer
- .parseInt(safeSubstring(versionString, 0, idx));
-
- int idx2 = versionString.indexOf('.', idx + 1);
- if (idx2 < 0) {
- idx2 = versionString.length();
- }
- try {
- browserMinorVersion = Integer
- .parseInt(safeSubstring(versionString, idx + 1, idx2)
- .replaceAll("[^0-9].*", ""));
- } catch (NumberFormatException e) {
- // leave the minor version unmodified (-1 = unknown)
- }
- }
-
- private String safeSubstring(String string, int beginIndex, int endIndex) {
- if (beginIndex < 0) {
- beginIndex = 0;
- }
- if (endIndex < 0 || endIndex > string.length()) {
- endIndex = string.length();
- }
- return string.substring(beginIndex, endIndex);
- }
-
- private String findBrowserVersion(String userAgent, int startIndex) {
- int index = startIndex;
- int length = userAgent.length();
-
- while (index < length) {
- char c = userAgent.charAt(index);
- // Accept letter, digit, underscore and dot characters
- if (!(Character.isLetter(c) || Character.isDigit(c) || c == '_'
- || c == '.')) {
- break;
- }
- index++;
- }
- return userAgent.substring(startIndex, index);
- }
-
- /**
- * Tests if the browser is Firefox.
- *
- * @return true if it is Firefox, false otherwise
- */
- public boolean isFirefox() {
- return isFirefox;
- }
-
- /**
- * Tests if the browser is using the Gecko engine.
- *
- * @return true if it is Gecko, false otherwise
- */
- public boolean isGecko() {
- return isGecko;
- }
-
- /**
- * Tests if the browser is using the WebKit engine.
- *
- * @return true if it is WebKit, false otherwise
- */
- public boolean isWebKit() {
- return isWebKit;
- }
-
- /**
- * Tests if the browser is using the Presto engine.
- *
- * @return true if it is Presto, false otherwise
- */
- public boolean isPresto() {
- return isPresto;
- }
-
- /**
- * Tests if the browser is using the Trident engine.
- *
- * @since 7.1.7
- * @return true if it is Trident, false otherwise
- */
- public boolean isTrident() {
- return isTrident;
- }
-
- /**
- * Tests if the browser is Safari.
- *
- * @return true if it is Safari, false otherwise
- */
- public boolean isSafari() {
- return isSafari;
- }
-
- /**
- * Tests if the browser is Safari or runs on IOS (covering also Chrome on
- * iOS).
- *
- * @return true if it is Safari or running on IOS, false otherwise
- * @since 8.1
- */
- public boolean isSafariOrIOS() {
- return isSafari() || isIOS();
- }
-
- /**
- * Tests if the browser is Chrome.
- *
- * @return true if it is Chrome, false otherwise
- */
- public boolean isChrome() {
- return isChrome;
- }
-
- /**
- * Tests if the browser is capable of running ChromeFrame.
- *
- * @return true if it has ChromeFrame, false otherwise
- */
- public boolean isChromeFrameCapable() {
- return isChromeFrameCapable;
- }
-
- /**
- * Tests if the browser is running ChromeFrame.
- *
- * @return true if it is ChromeFrame, false otherwise
- */
- public boolean isChromeFrame() {
- return isChromeFrame;
- }
-
- /**
- * Tests if the browser is Opera.
- *
- * @return true if it is Opera, false otherwise
- */
- public boolean isOpera() {
- return isOpera;
- }
-
- /**
- * Tests if the browser is Internet Explorer.
- *
- * @return true if it is Internet Explorer, false otherwise
- */
- public boolean isIE() {
- return isIE;
- }
-
- /**
- * Tests if the browser is Edge.
- *
- * @since 7.5.3
- * @return true if it is Edge, false otherwise
- */
- public boolean isEdge() {
- return isEdge;
- }
-
- /**
- * Tests if the browser is PhantomJS.
- *
- * @return true if it is PhantomJS, false otherwise
- */
- public boolean isPhantomJS() {
- return isPhantomJS;
- }
-
- /**
- * Returns the version of the browser engine. For WebKit this is an integer
- * e.g., 532.0. For gecko it is a float e.g., 1.8 or 1.9.
- *
- * @return The version of the browser engine
- */
- public float getBrowserEngineVersion() {
- return browserEngineVersion;
- }
-
- /**
- * Returns the browser major version e.g., 3 for Firefox 3.5, 4 for Chrome
- * 4, 8 for Internet Explorer 8.
- * <p>
- * Note that Internet Explorer 8 and newer will return the document mode so
- * IE8 rendering as IE7 will return 7.
- * </p>
- *
- * @return The major version of the browser.
- */
- public final int getBrowserMajorVersion() {
- return browserMajorVersion;
- }
-
- /**
- * Returns the browser minor version e.g., 5 for Firefox 3.5.
- *
- * @see #getBrowserMajorVersion()
- *
- * @return The minor version of the browser, or -1 if not known/parsed.
- */
- public final int getBrowserMinorVersion() {
- return browserMinorVersion;
- }
-
- /**
- * Gets the complete browser version as string.
- *
- * @return the complete browser version or {@code null} if unknown
- */
- public final String getBrowserVersion() {
- return browserVersion;
- }
-
- /**
- * Sets the version for IE based on the documentMode. This is used to return
- * the correct the correct IE version when the version from the user agent
- * string and the value of the documentMode property do not match.
- *
- * @param documentMode
- * The current document mode
- */
- public void setIEMode(int documentMode) {
- browserMajorVersion = documentMode;
- browserMinorVersion = 0;
- browserVersion = browserMajorVersion + "." + browserMinorVersion;
- }
-
- /**
- * Tests if the browser is run on Windows.
- *
- * @return true if run on Windows, false otherwise
- */
- public boolean isWindows() {
- return os == OperatingSystem.WINDOWS;
- }
-
- /**
- * Tests if the browser is run on Windows Phone.
- *
- * @return true if run on Windows Phone, false otherwise
- * @since 7.3.2
- */
- public boolean isWindowsPhone() {
- return isWindowsPhone;
- }
-
- /**
- * Tests if the browser is run on Mac OSX.
- *
- * @return true if run on Mac OSX, false otherwise
- */
- public boolean isMacOSX() {
- return os == OperatingSystem.MACOSX;
- }
-
- /**
- * Tests if the browser is run on Linux.
- *
- * @return true if run on Linux, false otherwise
- */
- public boolean isLinux() {
- return os == OperatingSystem.LINUX;
- }
-
- /**
- * Tests if the browser is run on Android.
- *
- * @return true if run on Android, false otherwise
- */
- public boolean isAndroid() {
- return os == OperatingSystem.ANDROID;
- }
-
- /**
- * Tests if the browser is run in iOS.
- *
- * @return true if run in iOS, false otherwise
- */
- public boolean isIOS() {
- return os == OperatingSystem.IOS;
- }
-
- /**
- * Tests if the browser is run on iPhone.
- *
- * @return true if run on iPhone, false otherwise
- * @since 7.3.3
- */
- public boolean isIPhone() {
- return isIPhone;
- }
-
- /**
- * Tests if the browser is run on iPad.
- *
- * @return true if run on iPad, false otherwise
- * @since 7.3.3
- */
- public boolean isIPad() {
- return isIPad;
- }
-
- /**
- * Tests if the browser is run on Chrome OS (e.g. a Chromebook).
- *
- * @return true if run on Chrome OS, false otherwise
- * @since 8.1.1
- */
- public boolean isChromeOS() {
- return isChromeOS;
- }
-
- /**
- * Returns the major version of the operating system. Currently only
- * supported for mobile devices (iOS/Android)
- *
- * @return The major version or -1 if unknown
- */
- public int getOperatingSystemMajorVersion() {
- return osMajorVersion;
- }
-
- /**
- * Returns the minor version of the operating system. Currently only
- * supported for mobile devices (iOS/Android)
- *
- * @return The minor version or -1 if unknown
- */
- public int getOperatingSystemMinorVersion() {
- return osMinorVersion;
- }
-
- /**
- * Checks if the browser is so old that it simply won't work with a Vaadin
- * application.
- *
- * @return true if the browser won't work, false if not the browser is
- * supported or might work
- */
- public boolean isTooOldToFunctionProperly() {
- if (isIE() && getBrowserMajorVersion() < 11) {
- return true;
- }
- // Webkit 533 in Safari 4.1+, Android 2.2+, iOS 4+
- // All iOS browsers use Safari as their engine.
- if ((isSafari() || isIOS()) && getBrowserEngineVersion() < 533) {
- return true;
- }
- // Firefox for iOS uses a different versioning scheme and will
- // fail the test. Since it is already covered by the iOS test
- // above, ignore it here.
- if (isFirefox() && !isIOS() && getBrowserMajorVersion() < 45) {
- return true;
- }
- if (isOpera() && getBrowserMajorVersion() < 11) {
- return true;
- }
-
- return false;
- }
-
- /**
- * Checks whether the browser should support ES6 based on its vendor and
- * version number.
- *
- * @return true if the browser supports ES6
- * @since 8.1
- */
- public boolean isEs6Supported() {
- // Safari 10+
- if (isSafari() && getBrowserMajorVersion() >= 10) {
- return true;
- }
- // Firefox 51+
- if (isFirefox() && getBrowserMajorVersion() >= 51) {
- return true;
- }
- // Opera 36+
- if (isOpera() && getBrowserMajorVersion() >= 36) {
- return true;
- }
- // Chrome 49+
- if (isChrome() && getBrowserMajorVersion() >= 49) {
- return true;
- }
- // Edge 15.15063+
- if (isEdge() && (getBrowserMajorVersion() > 15
- || (getBrowserMajorVersion() == 15
- && getBrowserMinorVersion() >= 15063))) {
- return true;
- }
-
- return false;
- }
-
- }
|