From 37903d2f5888a81117af33e52f571ee6ecf99d01 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Mar 2026 15:00:10 +0000 Subject: [PATCH] Validate Android WebView detection with Client Hints brands Co-authored-by: naorpeled <6171622+naorpeled@users.noreply.github.com> --- src/parser-browsers.js | 18 +++++++++++++++++- test/unit/parser.js | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/parser-browsers.js b/src/parser-browsers.js index cc45a10..5d8bef6 100644 --- a/src/parser-browsers.js +++ b/src/parser-browsers.js @@ -1095,12 +1095,28 @@ const browsersList = [ /* Android WebView */ { test(parser) { + // Client Hints: check for 'Android WebView' brand (Chromium-based WebViews) + if (parser.hasBrand('Android WebView')) { + return true; + } + // UA fallback: the canonical '; wv)' marker in Android WebView UA strings return parser.test(/android/i) && parser.test(/; wv\)/i); }, - describe(ua) { + describe(ua, parser) { const browser = { name: 'Chrome WebView', }; + + // Try Client Hints brand version first + if (parser) { + const hintsVersion = parser.getBrandVersion('Android WebView'); + if (hintsVersion) { + browser.version = hintsVersion; + return browser; + } + } + + // Fall back to Chrome version from UA string const version = Utils.getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.?_?\d+)+)/i, ua); if (version) { diff --git a/test/unit/parser.js b/test/unit/parser.js index a9bfb79..79b6b54 100644 --- a/test/unit/parser.js +++ b/test/unit/parser.js @@ -449,3 +449,37 @@ test('Parser.getBrandVersion returns version for Vivaldi', (t) => { const p = new Parser(VIVALDI_UA, false, VIVALDI_HINTS); t.is(p.getBrandVersion('Vivaldi'), '7.1'); }); + +const ANDROID_WEBVIEW_UA = 'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 5 Build/LMY48B; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/43.0.2357.65 Mobile Safari/537.36'; +const ANDROID_WEBVIEW_HINTS = { + brands: [ + { brand: 'Android WebView', version: '131' }, + { brand: 'Chromium', version: '131' }, + { brand: 'Not_A Brand', version: '24' }, + ], + mobile: true, + platform: 'Android', + platformVersion: '5.1.1', +}; + +test('Parser detects Chrome WebView from client hints brands', (t) => { + const p = new Parser(ANDROID_WEBVIEW_UA, false, ANDROID_WEBVIEW_HINTS); + t.is(p.getBrowserName(), 'Chrome WebView'); + t.is(p.getBrowserVersion(), '131'); +}); + +test('Parser detects Chrome WebView from UA string without client hints', (t) => { + const p = new Parser(ANDROID_WEBVIEW_UA); + t.is(p.getBrowserName(), 'Chrome WebView'); + t.is(p.getBrowserVersion(), '43.0.2357.65'); +}); + +test('Parser.hasBrand detects Android WebView', (t) => { + const p = new Parser(ANDROID_WEBVIEW_UA, false, ANDROID_WEBVIEW_HINTS); + t.true(p.hasBrand('Android WebView')); +}); + +test('Parser.getBrandVersion returns version for Android WebView', (t) => { + const p = new Parser(ANDROID_WEBVIEW_UA, false, ANDROID_WEBVIEW_HINTS); + t.is(p.getBrandVersion('Android WebView'), '131'); +});