From dc30825232a1355a325e364c8cd9fde78ffa3b1a Mon Sep 17 00:00:00 2001 From: undergroundwires Date: Wed, 3 Jan 2024 11:00:34 +0100 Subject: [PATCH] Fix macOS detection in desktop app and Chromium This commit addresses an issue where macOS was incorrectly identified as iPadOS in Chromium-based browsers. The root cause was related to touch support detection being inaccurately triggered on Chromium browsers, leading to misidentification. The bug caused two issues: 1. Desktop version: Script execution on macOS did not work as the desktop app wrongly assumed that it was running on iPadOS. 2. Web and desktop version: The UI didn't default to macOS, presuming an iPadOS environment. This bug was exclusive to Chromium browsers on macOS. Firefox and Safari didn't exhibit this behavior, as they handle touch event browser API as differently and initially expected. Key changes: - Improve touch support detection to accurately differentiate between macOS and iPadOS by removing an identification method used that is not reliable for Chromium-based browsers. - Update user agent detection to correctly identify Electron-based applications as macOS even without needing the information from the preloader context. --- .../BrowserOs/BrowserConditions.ts | 1 + .../TouchSupportDetection.ts | 37 +++++++++++-------- .../BrowserOs/BrowserOsTestCases.ts | 6 +++ .../Modules/MobileSafariDetectionTestCases.ts | 5 +++ .../TouchSupportDetection.spec.ts | 6 --- 5 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/infrastructure/RuntimeEnvironment/BrowserOs/BrowserConditions.ts b/src/infrastructure/RuntimeEnvironment/BrowserOs/BrowserConditions.ts index 26eda7fc..02608cd7 100644 --- a/src/infrastructure/RuntimeEnvironment/BrowserOs/BrowserConditions.ts +++ b/src/infrastructure/RuntimeEnvironment/BrowserOs/BrowserConditions.ts @@ -57,6 +57,7 @@ export const BrowserConditions: readonly BrowserCondition[] = [ { operatingSystem: OperatingSystem.iPadOS, existingPartsInSameUserAgent: ['Macintosh'], // Reported by Safari on iPads running ≥ iPadOS 13 + notExistingPartsInUserAgent: ['Electron'], // Electron supports only macOS, not iPadOS touchSupport: TouchSupportExpectation.MustExist, // Safari same user agent as desktop macOS }, { diff --git a/src/infrastructure/RuntimeEnvironment/TouchSupportDetection.ts b/src/infrastructure/RuntimeEnvironment/TouchSupportDetection.ts index a60f3549..b3581b7e 100644 --- a/src/infrastructure/RuntimeEnvironment/TouchSupportDetection.ts +++ b/src/infrastructure/RuntimeEnvironment/TouchSupportDetection.ts @@ -10,43 +10,48 @@ export interface BrowserTouchSupportAccessor { navigatorMaxTouchPoints: () => number | undefined; windowMatchMediaMatches: (query: string) => boolean; documentOntouchend: () => undefined | unknown; - windowTouchEvent: () => undefined | unknown; } +/* + Touch support checks are inconsistent across different browsers and OS. + `✅` and `❌` indicate correct and incorrect detections, respectively. +*/ const TouchSupportChecks: ReadonlyArray<(accessor: BrowserTouchSupportAccessor) => boolean> = [ /* - ✅ Mobile: Chrome, Safari, Firefox on iOS and Android - ❌ Touch-enabled Windows laptop: Chrome - (Chromium has removed ontouch* events on desktop since Chrome 70+.) - ❌ Touch-enabled Windows laptop: Firefox + Mobile (iOS & Android): ✅ Chrome, ✅ Safari, ✅ Firefox + Touch-enabled Windows laptop: ❌ Chrome (reports no touch), ❌ Firefox (reports no touch) + Chromium has removed ontouch* events on desktop since Chrome 70+ + Non-touch macOS: ✅ Firefox, ✅ Safari, ✅ Chromium */ (accessor) => accessor.documentOntouchend() !== undefined, /* - ✅ Mobile: Chrome, Safari, Firefox on iOS and Android - ✅ Touch-enabled Windows laptop: Chrome - ❌ Touch-enabled Windows laptop: Firefox + Mobile (iOS & Android): ✅ Chrome, ✅ Safari, ✅ Firefox + Touch-enabled Windows laptop: ✅ Chrome, ❌ Firefox (reports no touch) + Non-touch macOS: ✅ Firefox, ✅ Safari, ✅ Chromium */ (accessor) => { const maxTouchPoints = accessor.navigatorMaxTouchPoints(); return maxTouchPoints !== undefined && maxTouchPoints > 0; }, /* - ✅ Mobile: Chrome, Safari, Firefox on iOS and Android - ✅ Touch-enabled Windows laptop: Chrome - ❌ Touch-enabled Windows laptop: Firefox + Mobile (iOS & Android): ✅ Chrome, ✅ Safari, ✅ Firefox + Touch-enabled Windows laptop: ✅ Chrome, ❌ Firefox (reports no touch) + Non-touch macOS: ✅ Firefox, ✅ Safari, ✅ Chromium */ (accessor) => accessor.windowMatchMediaMatches('(any-pointer: coarse)'), + /* - ✅ Mobile: Chrome, Safari, Firefox on iOS and Android - ✅ Touch-enabled Windows laptop: Chrome - ❌ Touch-enabled Windows laptop: Firefox + Do not check window.TouchEvent === undefined, as it incorrectly + reports touch support on Chromium macOS even though there is no + touch support. + Mobile (iOS & Android): ✅ Chrome, ✅ Safari, ✅ Firefox + Touch-enabled Windows laptop: ✅ Chrome, ❌ Firefox (reports no touch) + Non-touch macOS: ✅ Firefox, ✅ Safari, ❌ Chromium (reports touch) */ - (accessor) => accessor.windowTouchEvent() !== undefined, ]; const GlobalTouchSupportAccessor: BrowserTouchSupportAccessor = { navigatorMaxTouchPoints: () => navigator.maxTouchPoints, windowMatchMediaMatches: (query: string) => window.matchMedia(query)?.matches, documentOntouchend: () => document.ontouchend, - windowTouchEvent: () => window.TouchEvent, } as const; diff --git a/tests/integration/infrastructure/RuntimeEnvironment/BrowserOs/BrowserOsTestCases.ts b/tests/integration/infrastructure/RuntimeEnvironment/BrowserOs/BrowserOsTestCases.ts index c4c670e0..71fb6492 100644 --- a/tests/integration/infrastructure/RuntimeEnvironment/BrowserOs/BrowserOsTestCases.ts +++ b/tests/integration/infrastructure/RuntimeEnvironment/BrowserOs/BrowserOsTestCases.ts @@ -37,6 +37,8 @@ export const BrowserOsTestCases: ReadonlyArray = [ 'Opera/9.80 (Windows NT 6.1; Opera Tablet/15165; U; en) Presto/2.8.149 Version/11.1', // UC Browser: 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 UBrowser/6.0.1308.1016 Safari/537.36', + // Electron: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.5993.54 Electron/27.0.0 Safari/537.36', ], }), ...createTests({ @@ -56,6 +58,8 @@ export const BrowserOsTestCases: ReadonlyArray = [ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.82 Safari/537.36 OPR/29.0.1795.41 (Edition beta)', // Edge: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0', + // Electron: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.5993.54 Electron/27.0.0 Safari/537.36', ], }), ...createTests({ @@ -68,6 +72,8 @@ export const BrowserOsTestCases: ReadonlyArray = [ 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36', // Edge: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.188', + // Electron: + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.5993.54 Electron/27.0.0 Safari/537.36', ], }), ...createTests({ diff --git a/tests/integration/presentation/bootstrapping/Modules/MobileSafariDetectionTestCases.ts b/tests/integration/presentation/bootstrapping/Modules/MobileSafariDetectionTestCases.ts index 8c1acbc8..58f6a0b1 100644 --- a/tests/integration/presentation/bootstrapping/Modules/MobileSafariDetectionTestCases.ts +++ b/tests/integration/presentation/bootstrapping/Modules/MobileSafariDetectionTestCases.ts @@ -58,6 +58,11 @@ export const MobileSafariDetectionTestCases: ReadonlyArray = [ operatingSystem: OperatingSystem.macOS, userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36', }, + { + deviceInfo: 'macOS (Electron)', + operatingSystem: OperatingSystem.macOS, + userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.5993.54 Electron/27.0.0 Safari/537.36', + }, { deviceInfo: 'iPad (iPadOS 17)', operatingSystem: OperatingSystem.iPadOS, diff --git a/tests/unit/infrastructure/RuntimeEnvironment/TouchSupportDetection.spec.ts b/tests/unit/infrastructure/RuntimeEnvironment/TouchSupportDetection.spec.ts index c2e8c379..778f267e 100644 --- a/tests/unit/infrastructure/RuntimeEnvironment/TouchSupportDetection.spec.ts +++ b/tests/unit/infrastructure/RuntimeEnvironment/TouchSupportDetection.spec.ts @@ -32,11 +32,6 @@ describe('TouchSupportDetection', () => { }), expectedTouch: true, }, - { - description: 'detects touch capability with defined window.TouchEvent', - expectedTouch: true, - accessor: createMockAccessor({ windowTouchEvent: () => class {} }), - }, ]; testScenarios.forEach(({ description, accessor, expectedTouch }) => { it(`${description} - returns ${expectedTouch}`, () => { @@ -56,7 +51,6 @@ function createMockAccessor( navigatorMaxTouchPoints: () => undefined, windowMatchMediaMatches: () => false, documentOntouchend: () => undefined, - windowTouchEvent: () => undefined, }; return { ...defaultTouchSupport,