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.
This commit is contained in:
@@ -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
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -37,6 +37,8 @@ export const BrowserOsTestCases: ReadonlyArray<BrowserOsTestCase> = [
|
||||
'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<BrowserOsTestCase> = [
|
||||
'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<BrowserOsTestCase> = [
|
||||
'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({
|
||||
|
||||
@@ -58,6 +58,11 @@ export const MobileSafariDetectionTestCases: ReadonlyArray<PlatformTestCase> = [
|
||||
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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user