Skip to content

Commit

Permalink
Fix macOS detection in desktop app and Chromium
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
undergroundwires committed Jan 3, 2024
1 parent 40f5eb8 commit dc30825
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
{
Expand Down
37 changes: 21 additions & 16 deletions src/infrastructure/RuntimeEnvironment/TouchSupportDetection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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({
Expand All @@ -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({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}`, () => {
Expand All @@ -56,7 +51,6 @@ function createMockAccessor(
navigatorMaxTouchPoints: () => undefined,
windowMatchMediaMatches: () => false,
documentOntouchend: () => undefined,
windowTouchEvent: () => undefined,
};
return {
...defaultTouchSupport,
Expand Down

0 comments on commit dc30825

Please sign in to comment.