All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.apkcloud.devicedetector.parser.client.Browser Maven / Gradle / Ivy

Go to download

通用设备检测库将解析任何UserAgent并检测浏览器、操作系统、使用的设备(桌面、平板、移动、电视、车载、游戏机等等)、品牌和型号。

There is a newer version: 1.0.7
Show newest version
package io.github.apkcloud.devicedetector.parser.client;

import io.github.apkcloud.devicedetector.ClientHints;
import io.github.apkcloud.devicedetector.entity.Client;
import io.github.apkcloud.devicedetector.entity.ClientEngine;
import io.github.apkcloud.devicedetector.parser.client.browser.Engine;
import io.github.apkcloud.devicedetector.parser.client.browser.engine.Version;
import io.github.apkcloud.devicedetector.parser.client.hints.BrowserHints;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 用于检测浏览器的客户端解析器
 */
public class Browser extends AbstractClientParser {

    private BrowserHints browserHints;

    /**
     * 包含正则表达式的 yml 文件的路径
     */
    private String fixtureFile = "regexes/client/browsers.yml";

    /**
     * 解析器的内部名称
     * 用于缓存
     */
    private String parserName = "browser";

    /**
     * 已知的浏览器映射到其内部短代码
     */
    protected static Map availableBrowsers = new HashMap<>() {
        {
            put("V1", "Via");
            put("1P", "Pure Mini Browser");
            put("4P", "Pure Lite Browser");
            put("1R", "Raise Fast Browser");
            put("R1", "Rabbit Private Browser");
            put("FQ", "Fast Browser UC Lite");
            put("FJ", "Fast Explorer");
            put("1L", "Lightning Browser");
            put("1C", "Cake Browser");
            put("1I", "IE Browser Fast");
            put("1V", "Vegas Browser");
            put("1O", "OH Browser");
            put("3O", "OH Private Browser");
            put("1X", "XBrowser Mini");
            put("1S", "Sharkee Browser");
            put("2L", "Lark Browser");
            put("3P", "Pluma");
            put("1A", "Anka Browser");
            put("AZ", "Azka Browser");
            put("1D", "Dragon Browser");
            put("1E", "Easy Browser");
            put("DW", "Dark Web Browser");
            put("18", "18+ Privacy Browser");
            put("1B", "115 Browser");
            put("DM", "1DM Browser");
            put("1M", "1DM+ Browser");
            put("2B", "2345 Browser");
            put("3B", "360 Browser");
            put("36", "360 Phone Browser");
            put("7B", "7654 Browser");
            put("AA", "Avant Browser");
            put("AB", "ABrowse");
            put("BW", "AdBlock Browser");
            put("A7", "Adult Browser");
            put("AF", "ANT Fresco");
            put("AG", "ANTGalio");
            put("AL", "Aloha Browser");
            put("AH", "Aloha Browser Lite");
            put("AM", "Amaya");
            put("A3", "Amaze Browser");
            put("A5", "Amerigo");
            put("AO", "Amigo");
            put("AN", "Android Browser");
            put("AE", "AOL Desktop");
            put("AD", "AOL Shield");
            put("A4", "AOL Shield Pro");
            put("A6", "AppBrowzer");
            put("AP", "APUS Browser");
            put("AR", "Arora");
            put("AX", "Arctic Fox");
            put("AV", "Amiga Voyager");
            put("AW", "Amiga Aweb");
            put("PN", "APN Browser");
            put("AI", "Arvin");
            put("AK", "Ask.com");
            put("AU", "Asus Browser");
            put("A0", "Atom");
            put("AT", "Atomic Web Browser");
            put("A2", "Atlas");
            put("AS", "Avast Secure Browser");
            put("VG", "AVG Secure Browser");
            put("AC", "Avira Scout");
            put("A1", "AwoX");
            put("BA", "Beaker Browser");
            put("BM", "Beamrise");
            put("BB", "BlackBerry Browser");
            put("H1", "BrowseHere");
            put("B8", "Browser Hup Pro");
            put("BD", "Baidu Browser");
            put("BS", "Baidu Spark");
            put("B9", "Bangla Browser");
            put("BI", "Basilisk");
            put("BV", "Belva Browser");
            put("B5", "Beyond Private Browser");
            put("BE", "Beonex");
            put("B2", "Berry Browser");
            put("BT", "Bitchute Browser");
            put("BH", "BlackHawk");
            put("B0", "Bloket");
            put("BJ", "Bunjalloo");
            put("BL", "B-Line");
            put("B6", "Black Lion Browser");
            put("BU", "Blue Browser");
            put("BO", "Bonsai");
            put("BN", "Borealis Navigator");
            put("BR", "Brave");
            put("BK", "BriskBard");
            put("B3", "Browspeed Browser");
            put("BX", "BrowseX");
            put("BZ", "Browzar");
            put("B7", "Browlser");
            put("BY", "Biyubi");
            put("BF", "Byffox");
            put("B4", "BF Browser");
            put("CA", "Camino");
            put("CL", "CCleaner");
            put("C8", "CG Browser");
            put("CJ", "ChanjetCloud");
            put("C6", "Chedot");
            put("C9", "Cherry Browser");
            put("C0", "Centaury");
            put("CC", "Coc Coc");
            put("C4", "CoolBrowser");
            put("C2", "Colibri");
            put("CD", "Comodo Dragon");
            put("C1", "Coast");
            put("CX", "Charon");
            put("CE", "CM Browser");
            put("C7", "CM Mini");
            put("CF", "Chrome Frame");
            put("HC", "Headless Chrome");
            put("CH", "Chrome");
            put("CI", "Chrome Mobile iOS");
            put("CK", "Conkeror");
            put("CM", "Chrome Mobile");
            put("3C", "Chowbo");
            put("CN", "CoolNovo");
            put("CO", "CometBird");
            put("2C", "Comfort Browser");
            put("CB", "COS Browser");
            put("CW", "Cornowser");
            put("C3", "Chim Lac");
            put("CP", "ChromePlus");
            put("CR", "Chromium");
            put("C5", "Chromium GOST");
            put("CY", "Cyberfox");
            put("CS", "Cheshire");
            put("CT", "Crusta");
            put("CG", "Craving Explorer");
            put("CZ", "Crazy Browser");
            put("CU", "Cunaguaro");
            put("CV", "Chrome Webview");
            put("YC", "CyBrowser");
            put("DB", "dbrowser");
            put("PD", "Peeps dBrowser");
            put("D1", "Debuggable Browser");
            put("DC", "Decentr");
            put("DE", "Deepnet Explorer");
            put("DG", "deg-degan");
            put("DA", "Deledao");
            put("DT", "Delta Browser");
            put("D0", "Desi Browser");
            put("DS", "DeskBrowse");
            put("DF", "Dolphin");
            put("DZ", "Dolphin Zero");
            put("DO", "Dorado");
            put("DR", "Dot Browser");
            put("DL", "Dooble");
            put("DI", "Dillo");
            put("DU", "DUC Browser");
            put("DD", "DuckDuckGo Privacy Browser");
            put("EC", "Ecosia");
            put("EW", "Edge WebView");
            put("EI", "Epic");
            put("EL", "Elinks");
            put("EN", "EinkBro");
            put("EB", "Element Browser");
            put("EE", "Elements Browser");
            put("EX", "Explore Browser");
            put("EZ", "eZ Browser");
            put("EU", "EUI Browser");
            put("EP", "GNOME Web");
            put("G1", "G Browser");
            put("ES", "Espial TV Browser");
            put("FA", "Falkon");
            put("FX", "Faux Browser");
            put("F4", "Fiery Browser");
            put("F1", "Firefox Mobile iOS");
            put("FB", "Firebird");
            put("FD", "Fluid");
            put("FE", "Fennec");
            put("FF", "Firefox");
            put("FK", "Firefox Focus");
            put("FY", "Firefox Reality");
            put("FR", "Firefox Rocket");
            put("1F", "Firefox Klar");
            put("F0", "Float Browser");
            put("FL", "Flock");
            put("FP", "Floorp");
            put("FO", "Flow");
            put("F2", "Flow Browser");
            put("FM", "Firefox Mobile");
            put("FW", "Fireweb");
            put("FN", "Fireweb Navigator");
            put("FH", "Flash Browser");
            put("FS", "Flast");
            put("F5", "Flyperlink");
            put("FU", "FreeU");
            put("F3", "Frost+");
            put("FI", "Fulldive");
            put("GA", "Galeon");
            put("G8", "Gener8");
            put("GH", "Ghostery Privacy Browser");
            put("GI", "GinxDroid Browser");
            put("GB", "Glass Browser");
            put("GE", "Google Earth");
            put("GP", "Google Earth Pro");
            put("GO", "GOG Galaxy");
            put("GR", "GoBrowser");
            put("HB", "Harman Browser");
            put("HS", "HasBrowser");
            put("HA", "Hawk Turbo Browser");
            put("HQ", "Hawk Quick Browser");
            put("HE", "Helio");
            put("HX", "Hexa Web Browser");
            put("HI", "Hi Browser");
            put("HO", "hola! Browser");
            put("HJ", "HotJava");
            put("HT", "HTC Browser");
            put("HU", "Huawei Browser Mobile");
            put("HP", "Huawei Browser");
            put("H3", "HUB Browser");
            put("IO", "iBrowser");
            put("IS", "iBrowser Mini");
            put("IB", "IBrowse");
            put("I6", "iDesktop PC Browser");
            put("IC", "iCab");
            put("I2", "iCab Mobile");
            put("I1", "Iridium");
            put("I3", "Iron Mobile");
            put("I4", "IceCat");
            put("ID", "IceDragon");
            put("IV", "Isivioo");
            put("I8", "IVVI Browser");
            put("IW", "Iceweasel");
            put("IN", "Inspect Browser");
            put("IE", "Internet Explorer");
            put("I7", "Internet Browser Secure");
            put("I5", "Indian UC Mini Browser");
            put("IM", "IE Mobile");
            put("IR", "Iron");
            put("JB", "Japan Browser");
            put("JS", "Jasmine");
            put("JA", "JavaFX");
            put("JL", "Jelly");
            put("JI", "Jig Browser");
            put("JP", "Jig Browser Plus");
            put("JO", "Jio Browser");
            put("J1", "JioPages");
            put("KB", "K.Browser");
            put("KF", "Keepsafe Browser");
            put("KS", "Kids Safe Browser");
            put("KI", "Kindle Browser");
            put("KM", "K-meleon");
            put("KO", "Konqueror");
            put("KP", "Kapiko");
            put("KN", "Kinza");
            put("KW", "Kiwi");
            put("KD", "Kode Browser");
            put("KT", "KUTO Mini Browser");
            put("KY", "Kylo");
            put("KZ", "Kazehakase");
            put("LB", "Cheetah Browser");
            put("LA", "Lagatos Browser");
            put("LR", "Lexi Browser");
            put("LV", "Lenovo Browser");
            put("LF", "LieBaoFast");
            put("LG", "LG Browser");
            put("LH", "Light");
            put("L1", "Lilo");
            put("LI", "Links");
            put("IF", "Lolifox");
            put("LO", "Lovense Browser");
            put("LT", "LT Browser");
            put("LU", "LuaKit");
            put("LL", "Lulumi");
            put("LS", "Lunascape");
            put("LN", "Lunascape Lite");
            put("LX", "Lynx");
            put("L2", "Lynket Browser");
            put("MD", "Mandarin");
            put("M1", "mCent");
            put("MB", "MicroB");
            put("MC", "NCSA Mosaic");
            put("MZ", "Meizu Browser");
            put("ME", "Mercury");
            put("M2", "Me Browser");
            put("MF", "Mobile Safari");
            put("MI", "Midori");
            put("M3", "Midori Lite");
            put("MO", "Mobicip");
            put("MU", "MIUI Browser");
            put("MS", "Mobile Silk");
            put("MN", "Minimo");
            put("MT", "Mint Browser");
            put("MX", "Maxthon");
            put("M4", "MaxTube Browser");
            put("MA", "Maelstrom");
            put("MM", "Mmx Browser");
            put("NM", "MxNitro");
            put("MY", "Mypal");
            put("MR", "Monument Browser");
            put("MW", "MAUI WAP Browser");
            put("NW", "Navigateur Web");
            put("NK", "Naked Browser");
            put("NA", "Naked Browser Pro");
            put("NR", "NFS Browser");
            put("NB", "Nokia Browser");
            put("NO", "Nokia OSS Browser");
            put("NV", "Nokia Ovi Browser");
            put("NX", "Nox Browser");
            put("N1", "NOMone VR Browser");
            put("NE", "NetSurf");
            put("NF", "NetFront");
            put("NL", "NetFront Life");
            put("NP", "NetPositive");
            put("NS", "Netscape");
            put("WR", "NextWord Browser");
            put("NT", "NTENT Browser");
            put("OC", "Oculus Browser");
            put("O1", "Opera Mini iOS");
            put("OB", "Obigo");
            put("O2", "Odin");
            put("2O", "Odin Browser");
            put("H2", "OceanHero");
            put("OD", "Odyssey Web Browser");
            put("OF", "Off By One");
            put("O5", "Office Browser");
            put("HH", "OhHai Browser");
            put("OE", "ONE Browser");
            put("Y1", "Opera Crypto");
            put("OX", "Opera GX");
            put("OG", "Opera Neon");
            put("OH", "Opera Devices");
            put("OI", "Opera Mini");
            put("OM", "Opera Mobile");
            put("OP", "Opera");
            put("ON", "Opera Next");
            put("OO", "Opera Touch");
            put("OA", "Orca");
            put("OS", "Ordissimo");
            put("OR", "Oregano");
            put("O0", "Origin In-Game Overlay");
            put("OY", "Origyn Web Browser");
            put("OV", "Openwave Mobile Browser");
            put("O3", "OpenFin");
            put("O4", "Open Browser");
            put("4U", "Open Browser 4U");
            put("5G", "Open Browser fast 5G");
            put("OW", "OmniWeb");
            put("OT", "Otter Browser");
            put("PL", "Palm Blazer");
            put("PM", "Pale Moon");
            put("PY", "Polypane");
            put("PP", "Oppo Browser");
            put("P6", "Opus Browser");
            put("PR", "Palm Pre");
            put("PU", "Puffin");
            put("2P", "Puffin Web Browser");
            put("PW", "Palm WebPro");
            put("PA", "Palmscape");
            put("PE", "Perfect Browser");
            put("P1", "Phantom.me");
            put("PH", "Phantom Browser");
            put("PX", "Phoenix");
            put("PB", "Phoenix Browser");
            put("PF", "PlayFree Browser");
            put("PK", "PocketBook Browser");
            put("PO", "Polaris");
            put("PT", "Polarity");
            put("LY", "PolyBrowser");
            put("PI", "PrivacyWall");
            put("P4", "Privacy Explorer Fast Safe");
            put("P2", "Pi Browser");
            put("P0", "PronHub Browser");
            put("PC", "PSI Secure Browser");
            put("RW", "Reqwireless WebViewer");
            put("PS", "Microsoft Edge");
            put("QA", "Qazweb");
            put("Q2", "QQ Browser Lite");
            put("Q1", "QQ Browser Mini");
            put("QQ", "QQ Browser");
            put("QS", "Quick Browser");
            put("QT", "Qutebrowser");
            put("QU", "Quark");
            put("QZ", "QupZilla");
            put("QM", "Qwant Mobile");
            put("QW", "QtWebEngine");
            put("RE", "Realme Browser");
            put("RK", "Rekonq");
            put("RM", "RockMelt");
            put("SB", "Samsung Browser");
            put("SA", "Sailfish Browser");
            put("S8", "Seewo Browser");
            put("SC", "SEMC-Browser");
            put("SE", "Sogou Explorer");
            put("SO", "Sogou Mobile Browser");
            put("RF", "SOTI Surf");
            put("2S", "Soul Browser");
            put("T0", "Soundy Browser");
            put("SF", "Safari");
            put("PV", "Safari Technology Preview");
            put("S5", "Safe Exam Browser");
            put("SW", "SalamWeb");
            put("VN", "Savannah Browser");
            put("SD", "SavySoda");
            put("S9", "Secure Browser");
            put("SV", "SFive");
            put("SH", "Shiira");
            put("K1", "Sidekick");
            put("S1", "SimpleBrowser");
            put("3S", "SilverMob US");
            put("SY", "Sizzy");
            put("SK", "Skyfire");
            put("SS", "Seraphic Sraf");
            put("KK", "SiteKiosk");
            put("SL", "Sleipnir");
            put("S6", "Slimjet");
            put("S7", "SP Browser");
            put("9S", "Sony Small Browser");
            put("8S", "Secure Private Browser");
            put("T1", "Stampy Browser");
            put("7S", "7Star");
            put("SQ", "Smart Browser");
            put("6S", "Smart Search & Web Browser");
            put("LE", "Smart Lenovo Browser");
            put("OZ", "Smooz");
            put("SN", "Snowshoe");
            put("B1", "Spectre Browser");
            put("S2", "Splash");
            put("SI", "Sputnik Browser");
            put("SR", "Sunrise");
            put("SP", "SuperBird");
            put("SU", "Super Fast Browser");
            put("5S", "SuperFast Browser");
            put("HR", "Sushi Browser");
            put("S3", "surf");
            put("4S", "Surf Browser");
            put("SG", "Stargon");
            put("S0", "START Internet Browser");
            put("S4", "Steam In-Game Overlay");
            put("ST", "Streamy");
            put("SX", "Swiftfox");
            put("SZ", "Seznam Browser");
            put("W1", "Sweet Browser");
            put("2X", "SX Browser");
            put("TP", "T+Browser");
            put("TR", "T-Browser");
            put("TO", "t-online.de Browser");
            put("TA", "Tao Browser");
            put("TF", "TenFourFox");
            put("TB", "Tenta Browser");
            put("TE", "Tesla Browser");
            put("TZ", "Tizen Browser");
            put("TI", "Tint Browser");
            put("TC", "TUC Mini Browser");
            put("TU", "Tungsten");
            put("TG", "ToGate");
            put("TS", "TweakStyle");
            put("TV", "TV Bro");
            put("U0", "U Browser");
            put("UB", "UBrowser");
            put("UC", "UC Browser");
            put("UH", "UC Browser HD");
            put("UM", "UC Browser Mini");
            put("UT", "UC Browser Turbo");
            put("UI", "Ui Browser Mini");
            put("UR", "UR Browser");
            put("UZ", "Uzbl");
            put("UE", "Ume Browser");
            put("V0", "vBrowser");
            put("VA", "Vast Browser");
            put("VE", "Venus Browser");
            put("N0", "Nova Video Downloader Pro");
            put("VS", "Viasat Browser");
            put("VI", "Vivaldi");
            put("VV", "vivo Browser");
            put("V2", "Vivid Browser Mini");
            put("VB", "Vision Mobile Browser");
            put("VM", "VMware AirWatch");
            put("WI", "Wear Internet Browser");
            put("WP", "Web Explorer");
            put("W3", "Web Browser & Explorer");
            put("WE", "WebPositive");
            put("WF", "Waterfox");
            put("WB", "Wave Browser");
            put("WA", "Wavebox");
            put("WH", "Whale Browser");
            put("WO", "wOSBrowser");
            put("WT", "WeTab Browser");
            put("WL", "Wolvic");
            put("YG", "YAGI");
            put("YJ", "Yahoo! Japan Browser");
            put("YA", "Yandex Browser");
            put("YL", "Yandex Browser Lite");
            put("YN", "Yaani Browser");
            put("Y2", "Yo Browser");
            put("YB", "Yolo Browser");
            put("YO", "YouCare");
            put("YZ", "Yuzu Browser");
            put("XR", "xBrowser");
            put("XB", "X Browser Lite");
            put("X0", "X-VPN");
            put("X1", "xBrowser Pro Super Fast");
            put("XN", "XNX Browser");
            put("XT", "XtremeCast");
            put("XS", "xStand");
            put("XI", "Xiino");
            put("XO", "Xooloo Internet");
            put("XV", "Xvast");
            put("ZE", "Zetakey");
            put("ZV", "Zvu");
            put("ZI", "Zirco Browser");

            // 在旧版本中检测到浏览器
            // put("IA", "Iceape");  => pim
            // put("SM", "SeaMonkey");  => pim
        }
    };

    /**
     * 浏览器家族映射到其相关的浏览器短代码
     */
    protected static Map> browserFamilies = new HashMap<>() {
        {
            put("Android Browser", Arrays.asList("AN", "MU"));
            put("BlackBerry Browser", Arrays.asList("BB"));
            put("Baidu", Arrays.asList("BD", "BS"));
            put("Amiga", Arrays.asList("AV", "AW"));
            put("Chrome", Arrays.asList(
                    "1B", "2B", "7S", "A0", "AC", "A4", "AE", "AH", "AI",
                    "AO", "AS", "BA", "BM", "BR", "C2", "C3", "C5", "C4",
                    "C6", "CC", "CD", "CE", "CF", "CG", "CH", "CI", "CL",
                    "CM", "CN", "CP", "CR", "CV", "CW", "DA", "DD", "DG",
                    "DR", "EC", "EE", "EU", "EW", "FA", "FS", "GB", "GI",
                    "H2", "HA", "HE", "HH", "HS", "I3", "IR", "JB", "KN",
                    "KW", "LF", "LL", "LO", "M1", "MA", "MD", "MR", "MS",
                    "MT", "MZ", "NM", "NR", "O0", "O2", "O3", "OC", "PB",
                    "PT", "QU", "QW", "RM", "S4", "S6", "S8", "S9", "SB",
                    "SG", "SS", "SU", "SV", "SW", "SY", "SZ", "T1", "TA",
                    "TB", "TG", "TR", "TS", "TU", "TV", "UB", "UR", "VE",
                    "VG", "VI", "VM", "WP", "WH", "XV", "YJ", "YN", "FH",
                    "B1", "BO", "HB", "PC", "LA", "LT", "PD", "HR", "HU",
                    "HP", "IO", "TP", "CJ", "HQ", "HI", "PN", "BW", "YO",
                    "DC", "G8", "DT", "AP", "AK", "UI", "SD", "VN", "4S",
                    "2S", "RF", "LR", "SQ", "BV", "L1", "F0", "KS", "V0",
                    "C8", "AZ", "MM", "BT", "N0", "P0", "F3", "VS", "DU",
                    "D0", "P1", "O4", "8S", "H3", "TE", "WB", "K1", "P2",
                    "XO", "U0", "B0", "VA", "X0", "NX", "O5", "R1", "I1",
                    "HO", "A5", "X1", "18", "B5", "B6", "TC", "A6", "2X",
                    "F4", "YG", "WR", "NA", "DM", "1M", "A7", "XN", "XT",
                    "XB", "W1", "HT", "B8", "F5", "B9", "WA", "T0", "HC"
            ));
            put("Firefox", Arrays.asList(
                    "AX", "BI", "BF", "BH", "BN", "C0", "CU", "EI", "F1",
                    "FB", "FE", "FF", "FM", "FR", "FY", "GZ", "I4", "IF",
                    "IW", "LH", "LY", "MB", "MN", "MO", "MY", "OA", "OS",
                    "PI", "PX", "QA", "QM", "S5", "SX", "TF", "TO", "WF",
                    "ZV", "FP", "AD", "WL"
            ));
            put("Internet Explorer", Arrays.asList("BZ", "CZ", "IE", "IM", "PS"));
            put("Konqueror", Arrays.asList("KO"));
            put("NetFront", Arrays.asList("NF"));
            put("NetSurf", Arrays.asList("NE"));
            put("Nokia Browser", Arrays.asList("DO", "NB", "NO", "NV"));
            put("Opera", Arrays.asList("O1", "OG", "OH", "OI", "OM", "ON", "OO", "OP", "OX", "Y1"));
            put("Safari", Arrays.asList("MF", "S7", "SF", "SO", "PV"));
            put("Sailfish Browser", Arrays.asList("SA"));
        }
    };

    /**
     * 仅适用于移动设备的浏览器
     */
    protected static List mobileOnlyBrowsers = Arrays.asList(
            "36", "AH", "AI", "BL", "C1", "C4", "CB", "CW", "DB",
            "DD", "DT", "EU", "EZ", "FK", "FM", "FR", "FX", "GH",
            "GI", "GR", "HA", "HU", "IV", "JB", "KD", "M1", "MF",
            "MN", "MZ", "NX", "OC", "OI", "OM", "OZ", "PU", "PI",
            "PE", "QU", "RE", "S0", "S7", "SA", "SB", "SG", "SK",
            "ST", "SU", "T1", "UH", "UM", "UT", "VE", "VV", "WI",
            "WP", "YN", "IO", "IS", "HQ", "RW", "HI", "PN", "BW",
            "YO", "PK", "MR", "AP", "AK", "UI", "SD", "VN", "4S",
            "RF", "LR", "SQ", "BV", "L1", "F0", "KS", "V0", "C8",
            "AZ", "MM", "BT", "N0", "P0", "F3", "DU", "D0", "P1",
            "O4", "XO", "U0", "B0", "VA", "X0", "A5", "X1", "18",
            "B5", "B6", "TC", "A6", "2X", "F4", "YG", "WR", "NA",
            "DM", "1M", "A7", "XN", "XT", "XB", "W1", "HT", "B7",
            "B9", "T0", "I8"
    );

    /**
     * 包含从我们使用的操作系统名称到已知 ClientHints 值的映射列表
     */
    protected static Map> clientHintMapping = new LinkedHashMap<>(){
        {
            put("Chrome", Arrays.asList("Google Chrome"));
        }
    };

    public Browser() {
        super();
        this.browserHints = new BrowserHints();
    }

    public Browser(String ua) {
        super(ua);
        this.browserHints = new BrowserHints(ua);
    }

    public Browser(String ua, ClientHints clientHints) {
        super(ua, clientHints);
        this.browserHints = new BrowserHints(ua, clientHints);
    }

    /**
     * 设置 ClientHints
     */
    public void setClientHints(ClientHints clientHints) {
        super.setClientHints(clientHints);
        this.browserHints.setClientHints(clientHints);
    }

    /**
     * 设置要解析的 UserAgent
     */
    public void setUserAgent(String ua) {
        super.setUserAgent(ua);
        this.browserHints.setUserAgent(ua);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getName() {
        return parserName;
    }

    /**
     * 返回所有可用的浏览器列表
     *
     * @return {@code Map}
     */
    public static Map getAvailableBrowsers() {
        return availableBrowsers;
    }

    /**
     * 返回所有可用的浏览器家族列表
     *
     * @return {@code Map>}
     */
    public static Map> getAvailableBrowserFamilies() {
        return browserFamilies;
    }

    /**
     * 根据浏览器名称返回浏览器的短代码
     * @param name 浏览器名称
     * @return String | null
     */
    public static String getBrowserShortName(String name) {
        for (Map.Entry entry : getAvailableBrowsers().entrySet()) {
            if (name.equalsIgnoreCase(entry.getValue())) {
                return entry.getKey();
            }
        }

        return null;
    }

    /**
     * 根据浏览器的名称或短代码返回浏览器家族
     *
     * @param browserLabel 名称或短代码
     *
     * @return String|null If null, "Unknown"
     */
    public static String getBrowserFamily(String browserLabel) {
        if (availableBrowsers.containsValue(browserLabel)) {
            browserLabel = getKeyByValue(availableBrowsers, browserLabel);
        }

        for (Map.Entry> entry : browserFamilies.entrySet()) {
            if (entry.getValue().contains(browserLabel)) {
                return entry.getKey();
            }
        }

        return null;
    }

    /**
     * 判断给定的浏览器是不是仅限移动设备的浏览器
     *
     * @param browser 标签或浏览器名称
     *
     * @return boolean
     */
    public static boolean isMobileOnlyBrowser(String browser) {
        return mobileOnlyBrowsers.contains(browser)
                || (availableBrowsers.containsValue(browser) && mobileOnlyBrowsers.contains(getKeyByValue(availableBrowsers, browser)));
    }

    /**
     * {@inheritDoc}
     */
    public Map parse() throws Exception {
        Map browserFromClientHints = parseBrowserFromClientHints();
        Map browserFromUserAgent = parseBrowserFromUserAgent();
        String name = "", version = "", shortName = "", engine = "", engineVersion = "";

        // 在可能的情况下使用客户端提示而不是用户代理数据
        if (!isNullOrEmpty(browserFromClientHints.get("name")) && !isNullOrEmpty(browserFromClientHints.get("version"))) {
            name = browserFromClientHints.get("name");
            version = browserFromClientHints.get("version");
            shortName = browserFromClientHints.get("short_name");
            engine = "";
            engineVersion = "";

            // 如果来自 ClientHints 的版本为 2022 或 2022.04,则为 Iridium 浏览器
            // https://iridiumbrowser.de/news/2022/05/16/version-2022-04-released
            if ("2021.12".equals(version) || "2022".equals(version) || "2022.04".equals(version)) {
                name = "Iridium";
                shortName = "I1";
                engine = browserFromUserAgent.get("engine");
                engineVersion = browserFromUserAgent.get("engine_version");
            }

            if ("Atom".equals(name) || "Huawei Browser".equals(name)) {
                version = browserFromUserAgent.get("version");
            }

            // 如果 ClientHints 的报告为 Chromium,但 UserAgent 的检测到基于 Chromium 的浏览器,我们优先使用后者
            if ("Chromium".equals(name)
                    && !isNullOrEmpty(browserFromUserAgent.get("name"))
                    && !"Chromium".equals(browserFromUserAgent.get("name"))) {
                name = browserFromUserAgent.get("name");
                shortName = browserFromUserAgent.get("short_name");
                version = browserFromUserAgent.get("version");
            }

            // 修复移动浏览器名称,例如 Chrome => Chrome Mobile
            if ((name + " Mobile").equals(browserFromUserAgent.get("name"))) {
                name = browserFromUserAgent.get("name");
                shortName = browserFromUserAgent.get("short_name");
            }

            // 如果在 UserAgent 中检测到另一个浏览器,但家族匹配,我们将使用 UserAgent 中检测到的引擎
            if (!name.equals(browserFromUserAgent.get("name"))
                    && Objects.equals(getBrowserFamily(name), getBrowserFamily(browserFromUserAgent.get("name")))) {
                engine = browserFromUserAgent.getOrDefault("engine", "");
                engineVersion = browserFromUserAgent.getOrDefault("engine_version", "");
            }

            if (name.equals(browserFromUserAgent.get("name"))) {
                engine = browserFromUserAgent.getOrDefault("engine", "");
                engineVersion = browserFromUserAgent.getOrDefault("engine_version", "");

                // 如果 UserAgent 报告了更详细的版本,我们会尝试使用它来替代
                if (!isNullOrEmpty(browserFromUserAgent.get("version"))
                        && browserFromUserAgent.get("version").startsWith(version)
                        && compareVersions(version, browserFromUserAgent.get("version")) == -1) {
                    version = browserFromUserAgent.get("version");
                }
            }
        } else {
            name = browserFromUserAgent.get("name");
            version = browserFromUserAgent.get("version");
            shortName = browserFromUserAgent.get("short_name");
            engine = browserFromUserAgent.get("engine");
            engineVersion = browserFromUserAgent.get("engine_version");
        }

        String family = getBrowserFamily(shortName);
        Map appHash = this.browserHints.parse();

        if (appHash != null && !Objects.equals(name, appHash.get("name"))) {
            name = appHash.getOrDefault("name", "");
            version = "";
            shortName = getBrowserShortName(name);

            Matcher matcher = Pattern.compile("Chrome/.+ Safari/537.36", Pattern.CASE_INSENSITIVE).matcher(userAgent);
            if (matcher.find()) {
                engine = "Blink";
                family = getBrowserFamily(shortName) != null ? getBrowserFamily(shortName) : "Chrome";
                engineVersion = buildEngineVersion(engine);
            }

            if (shortName == null) {
                // 此异常不应该抛出。如果发生这种情况,则在 availableBrowsers 中缺少定义的浏览器名称
                throw new Exception(String.format(
                        "在 availableBrowsers 中找不到检测到的浏览器名称“%s”。尝试解析UserAgent:%s",
                        name,
                        userAgent)
                ); // @codeCoverageIgnore
            }
        }

        if (isNullOrEmpty(name)) {
            return Collections.emptyMap();
        }

        // 对于浏览器,排除 Blink 引擎版本
        if ("Blink".equals(engine) && "Flow Browser".equals(name)) {
            engineVersion = "";
        }

        Map result = new HashMap<>();
        result.put("type", "browser");
        result.put("name", name);
        result.put("short_name", shortName);
        result.put("version", version);
        result.put("engine", engine);
        result.put("engine_version", engineVersion);
        result.put("family", family);

        return result;
    }

    /**
     * 返回从 ClientHints 中可以有效检测到的浏览器
     *
     * @return {@code Map}
     */
    protected Map parseBrowserFromClientHints() {
        String name = "", version = "", shortName = "";

        if (clientHints != null && !isNullOrEmpty(clientHints.getBrandList())) {
            Map brands = clientHints.getBrandList();

            for (Map.Entry entry : brands.entrySet()) {
                String brand = applyClientHintMapping(entry.getKey(), clientHintMapping);

                for (Map.Entry browserEntry : availableBrowsers.entrySet()) {
                    String browserShort = browserEntry.getKey();
                    String browserName = browserEntry.getValue();

                    if (fuzzyCompare(brand, browserName) || fuzzyCompare(brand + " Browser", browserName) || fuzzyCompare(brand, browserName + " Browser")) {
                        name = browserName;
                        shortName = browserShort;
                        version = entry.getValue();

                        break;
                    }
                }

                // 如果检测到的品牌不是 Chromium,我们将使用它,否则我们将继续寻找
                if (!"".equals(name) && !"Chromium".equals(name)) {
                    break;
                }
            }

            version = !isNullOrEmpty(clientHints.getBrandVersion()) ? clientHints.getBrandVersion() : version;
        }

        Map result = new HashMap<>();
        result.put("name", name);
        result.put("short_name", shortName);
        result.put("version", buildVersion(version, Collections.emptyList()));

        return result;
    }

    /**
     * 返回从 UserAgent 中可以检测到的浏览器
     *
     * @return {@code Map}
     *
     * @throws Exception
     */
    protected Map parseBrowserFromUserAgent() throws Exception {
        Client regex = null;
        List matches = null;

        List regexes = getRegexes(fixtureFile, Client.class);
        for (Client client : regexes) {
            regex = client;
            matches = matchUserAgent(client.getRegex());

            if (!isNullOrEmpty(matches)) {
                break;
            }
        }

        if (isNullOrEmpty(matches) || regex == null) {
            Map result = new HashMap<>();
            result.put("name", "");
            result.put("short_name", "");
            result.put("version", "");
            result.put("engine", "");
            result.put("engine_version", "");
            return result;
        }

        String name = buildByMatch(regex.getName(), matches);
        String browserShort = getBrowserShortName(name);

        if (browserShort != null) {
            String version = buildVersion(regex.getVersion(), matches);
            String engine = buildEngine(regex.getEngine(), version);
            String engineVersion = buildEngineVersion(engine);

            Map result = new HashMap<>();
            result.put("name", name);
            result.put("short_name", browserShort);
            result.put("version", version);
            result.put("engine", engine);
            result.put("engine_version", engineVersion);

            return result;
        }

        // 此异常不应该抛出。如果发生这种情况,则在 availableBrowsers 中缺少定义的浏览器名称
        throw new Exception(String.format(
                "在 availableBrowsers 中找不到检测到的浏览器名称“%s“。尝试解析UserAgent:%s",
                name,
                userAgent)); // @codeCoverageIgnore
    }

    /**
     * @param engineData
     * @param browserVersion
     * @return String
     */
    protected String buildEngine(ClientEngine engineData, String browserVersion) throws Exception {
        String engine = "";

        // 如果引擎设置为默认
        if (engineData != null && engineData.getDefault() != null) {
            engine = engineData.getDefault();
        }

        // 检查引擎是否设置了浏览器版本
        if (engineData != null && engineData.getVersions() != null) {
            for (Map.Entry entry : engineData.getVersions().entrySet()) {
                String version = entry.getKey();
                String versionEngine = entry.getValue();

                if (compareVersions(browserVersion, version) < 0) {
                    continue;
                }

                engine = versionEngine;
            }
        }

        // 尝试使用正则表达式检测引擎
        if (isNullOrEmpty(engine)) {
            Engine engineParser = new Engine();
            engineParser.setUserAgent(userAgent);
            Map result = engineParser.parse();
            engine = result.getOrDefault("engine", "");
        }

        return engine;
    }

    /**
     * @param engine
     * @return String
     */
    protected String buildEngineVersion(String engine) {
        Version engineVersionParser = new Version(userAgent, engine);
        Map result = engineVersionParser.parse();
        return result.getOrDefault("version", "");
    }

}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy