From f008ec9958b34d247b4d526a994f1003c7eb348d Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Tue, 11 May 2021 13:59:01 -0700 Subject: [PATCH] [web] Resolve OS as iOs for iDevice Safari requesting desktop version of app. (flutter/engine#25957) --- .../lib/src/engine/browser_detection.dart | 20 +- .../lib/web_ui/test/browser_detect_test.dart | 174 +++++++++++++++--- 2 files changed, 161 insertions(+), 33 deletions(-) diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/browser_detection.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/browser_detection.dart index ea6d44eb12..c085d72ffa 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/browser_detection.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/browser_detection.dart @@ -131,7 +131,7 @@ enum OperatingSystem { } /// Lazily initialized current operating system. -late final OperatingSystem _operatingSystem = _detectOperatingSystem(); +late final OperatingSystem _operatingSystem = detectOperatingSystem(); /// Returns the [OperatingSystem] the current browsers works on. /// @@ -149,11 +149,23 @@ OperatingSystem get operatingSystem { /// This is intended to be used for testing and debugging only. OperatingSystem? debugOperatingSystemOverride; -OperatingSystem _detectOperatingSystem() { - final String platform = html.window.navigator.platform!; - final String userAgent = html.window.navigator.userAgent; +@visibleForTesting +OperatingSystem detectOperatingSystem({ + String? overridePlatform, + String? overrideUserAgent, + int? overrideMaxTouchPoints, +}) { + final String platform = overridePlatform ?? html.window.navigator.platform!; + final String userAgent = overrideUserAgent ?? html.window.navigator.userAgent; if (platform.startsWith('Mac')) { + // iDevices requesting a "desktop site" spoof their UA so it looks like a Mac. + // This checks if we're in a touch device, or on a real mac. + final int maxTouchPoints = + overrideMaxTouchPoints ?? html.window.navigator.maxTouchPoints ?? 0; + if (maxTouchPoints > 2) { + return OperatingSystem.iOs; + } return OperatingSystem.macOs; } else if (platform.toLowerCase().contains('iphone') || platform.toLowerCase().contains('ipad') || diff --git a/engine/src/flutter/lib/web_ui/test/browser_detect_test.dart b/engine/src/flutter/lib/web_ui/test/browser_detect_test.dart index dec744e32e..43a8402ac0 100644 --- a/engine/src/flutter/lib/web_ui/test/browser_detect_test.dart +++ b/engine/src/flutter/lib/web_ui/test/browser_detect_test.dart @@ -11,38 +11,154 @@ void main() { } void testMain() { - test('Should detect Blink', () { - // Chrome Version 89.0.4389.90 (Official Build) (x86_64) / MacOS - BrowserEngine browserEngine = detectBrowserEngineByVendorAgent( - 'Google Inc.', - 'mozilla/5.0 (macintosh; intel mac os x 11_2_3) applewebkit/537.36 ' - '(khtml, like gecko) chrome/89.0.4389.90 safari/537.36'); - expect(browserEngine, BrowserEngine.blink); + group('detectBrowserEngineByVendorAgent', () { + test('Should detect Blink', () { + // Chrome Version 89.0.4389.90 (Official Build) (x86_64) / MacOS + BrowserEngine browserEngine = detectBrowserEngineByVendorAgent( + 'Google Inc.', + 'mozilla/5.0 (macintosh; intel mac os x 11_2_3) applewebkit/537.36 ' + '(khtml, like gecko) chrome/89.0.4389.90 safari/537.36'); + expect(browserEngine, BrowserEngine.blink); + }); + + test('Should detect Firefox', () { + // 85.0.2 (64-bit) / MacOS + BrowserEngine browserEngine = detectBrowserEngineByVendorAgent( + '', + 'mozilla/5.0 (macintosh; intel mac os x 10.16; rv:85.0) ' + 'gecko/20100101 firefox/85.0'); + expect(browserEngine, BrowserEngine.firefox); + }); + + test('Should detect Safari', () { + BrowserEngine browserEngine = detectBrowserEngineByVendorAgent( + 'Apple Computer, Inc.', + 'mozilla/5.0 (macintosh; intel mac os x 10_15_6) applewebkit/605.1.15 ' + '(khtml, like gecko) version/14.0.3 safari/605.1.15'); + expect(browserEngine, BrowserEngine.webkit); + }); + + test('Should detect Samsung browser', () { + // Samsung 13.2.1.70 on Galaxy Tab S6. + BrowserEngine browserEngine = detectBrowserEngineByVendorAgent( + 'Google Inc.', + 'mozilla/5.0 (x11; linux x86_64) applewebkit/537.36 (khtml, like gecko)' + ' samsungbrowser/13.2 chrome/83.0.4103.106 safari/537.36'); + expect(browserEngine, BrowserEngine.samsung); + }); }); - test('Should detect Firefox', () { - // 85.0.2 (64-bit) / MacOS - BrowserEngine browserEngine = detectBrowserEngineByVendorAgent( - '', - 'mozilla/5.0 (macintosh; intel mac os x 10.16; rv:85.0) ' - 'gecko/20100101 firefox/85.0'); - expect(browserEngine, BrowserEngine.firefox); - }); + group('detectOperatingSystem', () { + void expectOs( + OperatingSystem expectedOs, { + String platform = 'any', + String ua = 'any', + int touchPoints = 0, + }) { + expect( + detectOperatingSystem( + overridePlatform: platform, + overrideUserAgent: ua, + overrideMaxTouchPoints: touchPoints, + ), + expectedOs); + } - test('Should detect Safari', () { - BrowserEngine browserEngine = detectBrowserEngineByVendorAgent( - 'Apple Computer, Inc.', - 'mozilla/5.0 (macintosh; intel mac os x 10_15_6) applewebkit/605.1.15 ' - '(khtml, like gecko) version/14.0.3 safari/605.1.15'); - expect(browserEngine, BrowserEngine.webkit); - }); + test('Determine unknown for weird values of platform/ua', () { + expectOs(OperatingSystem.unknown); + }); - test('Should detect Samsung browser', () { - // Samsung 13.2.1.70 on Galaxy Tab S6. - BrowserEngine browserEngine = detectBrowserEngineByVendorAgent( - 'Google Inc.', - 'mozilla/5.0 (x11; linux x86_64) applewebkit/537.36 (khtml, like gecko)' - ' samsungbrowser/13.2 chrome/83.0.4103.106 safari/537.36'); - expect(browserEngine, BrowserEngine.samsung); + test('Determine MacOS if platform starts by Mac', () { + expectOs( + OperatingSystem.macOs, + platform: 'MacIntel', + ); + expectOs( + OperatingSystem.macOs, + platform: 'MacAnythingElse', + ); + }); + + test('Determine iOS if platform contains iPhone/iPad/iPod', () { + expectOs( + OperatingSystem.iOs, + platform: 'iPhone', + ); + expectOs( + OperatingSystem.iOs, + platform: 'iPhone Simulator', + ); + expectOs( + OperatingSystem.iOs, + platform: 'iPad', + ); + expectOs( + OperatingSystem.iOs, + platform: 'iPad Simulator', + ); + expectOs( + OperatingSystem.iOs, + platform: 'iPod', + ); + expectOs( + OperatingSystem.iOs, + platform: 'iPod Simulator', + ); + }); + + // See https://github.com/flutter/flutter/issues/81918 + test('Tell apart MacOS from iOS requesting a desktop site.', () { + expectOs( + OperatingSystem.macOs, + platform: 'MacARM', + ); + + expectOs( + OperatingSystem.iOs, + platform: 'MacARM', + touchPoints: 5, + ); + }); + + test('Determine Android if user agent contains Android', () { + expectOs( + OperatingSystem.android, + ua: 'Mozilla/5.0 (Linux; U; Android 2.2) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1', + ); + }); + + test('Determine Linux if the platform begins with Linux', () { + expectOs( + OperatingSystem.linux, + platform: 'Linux', + ); + expectOs( + OperatingSystem.linux, + platform: 'Linux armv8l', + ); + expectOs( + OperatingSystem.linux, + platform: 'Linux x86_64', + ); + }); + + test('Determine Windows if the platform begins with Win', () { + expectOs( + OperatingSystem.windows, + platform: 'Windows', + ); + expectOs( + OperatingSystem.windows, + platform: 'Win32', + ); + expectOs( + OperatingSystem.windows, + platform: 'Win16', + ); + expectOs( + OperatingSystem.windows, + platform: 'WinCE', + ); + }); }); }