Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 3c93ad4

Browse files
authored
[web] Resolve OS as iOs for iDevice Safari requesting desktop version of app. (#25957)
1 parent af0a96b commit 3c93ad4

File tree

2 files changed

+161
-33
lines changed

2 files changed

+161
-33
lines changed

lib/web_ui/lib/src/engine/browser_detection.dart

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ enum OperatingSystem {
131131
}
132132

133133
/// Lazily initialized current operating system.
134-
late final OperatingSystem _operatingSystem = _detectOperatingSystem();
134+
late final OperatingSystem _operatingSystem = detectOperatingSystem();
135135

136136
/// Returns the [OperatingSystem] the current browsers works on.
137137
///
@@ -149,11 +149,23 @@ OperatingSystem get operatingSystem {
149149
/// This is intended to be used for testing and debugging only.
150150
OperatingSystem? debugOperatingSystemOverride;
151151

152-
OperatingSystem _detectOperatingSystem() {
153-
final String platform = html.window.navigator.platform!;
154-
final String userAgent = html.window.navigator.userAgent;
152+
@visibleForTesting
153+
OperatingSystem detectOperatingSystem({
154+
String? overridePlatform,
155+
String? overrideUserAgent,
156+
int? overrideMaxTouchPoints,
157+
}) {
158+
final String platform = overridePlatform ?? html.window.navigator.platform!;
159+
final String userAgent = overrideUserAgent ?? html.window.navigator.userAgent;
155160

156161
if (platform.startsWith('Mac')) {
162+
// iDevices requesting a "desktop site" spoof their UA so it looks like a Mac.
163+
// This checks if we're in a touch device, or on a real mac.
164+
final int maxTouchPoints =
165+
overrideMaxTouchPoints ?? html.window.navigator.maxTouchPoints ?? 0;
166+
if (maxTouchPoints > 2) {
167+
return OperatingSystem.iOs;
168+
}
157169
return OperatingSystem.macOs;
158170
} else if (platform.toLowerCase().contains('iphone') ||
159171
platform.toLowerCase().contains('ipad') ||

lib/web_ui/test/browser_detect_test.dart

Lines changed: 145 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,38 +11,154 @@ void main() {
1111
}
1212

1313
void testMain() {
14-
test('Should detect Blink', () {
15-
// Chrome Version 89.0.4389.90 (Official Build) (x86_64) / MacOS
16-
BrowserEngine browserEngine = detectBrowserEngineByVendorAgent(
17-
'Google Inc.',
18-
'mozilla/5.0 (macintosh; intel mac os x 11_2_3) applewebkit/537.36 '
19-
'(khtml, like gecko) chrome/89.0.4389.90 safari/537.36');
20-
expect(browserEngine, BrowserEngine.blink);
21-
});
14+
group('detectBrowserEngineByVendorAgent', () {
15+
test('Should detect Blink', () {
16+
// Chrome Version 89.0.4389.90 (Official Build) (x86_64) / MacOS
17+
BrowserEngine browserEngine = detectBrowserEngineByVendorAgent(
18+
'Google Inc.',
19+
'mozilla/5.0 (macintosh; intel mac os x 11_2_3) applewebkit/537.36 '
20+
'(khtml, like gecko) chrome/89.0.4389.90 safari/537.36');
21+
expect(browserEngine, BrowserEngine.blink);
22+
});
2223

23-
test('Should detect Firefox', () {
24-
// 85.0.2 (64-bit) / MacOS
25-
BrowserEngine browserEngine = detectBrowserEngineByVendorAgent(
26-
'',
27-
'mozilla/5.0 (macintosh; intel mac os x 10.16; rv:85.0) '
28-
'gecko/20100101 firefox/85.0');
29-
expect(browserEngine, BrowserEngine.firefox);
30-
});
24+
test('Should detect Firefox', () {
25+
// 85.0.2 (64-bit) / MacOS
26+
BrowserEngine browserEngine = detectBrowserEngineByVendorAgent(
27+
'',
28+
'mozilla/5.0 (macintosh; intel mac os x 10.16; rv:85.0) '
29+
'gecko/20100101 firefox/85.0');
30+
expect(browserEngine, BrowserEngine.firefox);
31+
});
32+
33+
test('Should detect Safari', () {
34+
BrowserEngine browserEngine = detectBrowserEngineByVendorAgent(
35+
'Apple Computer, Inc.',
36+
'mozilla/5.0 (macintosh; intel mac os x 10_15_6) applewebkit/605.1.15 '
37+
'(khtml, like gecko) version/14.0.3 safari/605.1.15');
38+
expect(browserEngine, BrowserEngine.webkit);
39+
});
3140

32-
test('Should detect Safari', () {
33-
BrowserEngine browserEngine = detectBrowserEngineByVendorAgent(
34-
'Apple Computer, Inc.',
35-
'mozilla/5.0 (macintosh; intel mac os x 10_15_6) applewebkit/605.1.15 '
36-
'(khtml, like gecko) version/14.0.3 safari/605.1.15');
37-
expect(browserEngine, BrowserEngine.webkit);
41+
test('Should detect Samsung browser', () {
42+
// Samsung 13.2.1.70 on Galaxy Tab S6.
43+
BrowserEngine browserEngine = detectBrowserEngineByVendorAgent(
44+
'Google Inc.',
45+
'mozilla/5.0 (x11; linux x86_64) applewebkit/537.36 (khtml, like gecko)'
46+
' samsungbrowser/13.2 chrome/83.0.4103.106 safari/537.36');
47+
expect(browserEngine, BrowserEngine.samsung);
48+
});
3849
});
3950

40-
test('Should detect Samsung browser', () {
41-
// Samsung 13.2.1.70 on Galaxy Tab S6.
42-
BrowserEngine browserEngine = detectBrowserEngineByVendorAgent(
43-
'Google Inc.',
44-
'mozilla/5.0 (x11; linux x86_64) applewebkit/537.36 (khtml, like gecko)'
45-
' samsungbrowser/13.2 chrome/83.0.4103.106 safari/537.36');
46-
expect(browserEngine, BrowserEngine.samsung);
51+
group('detectOperatingSystem', () {
52+
void expectOs(
53+
OperatingSystem expectedOs, {
54+
String platform = 'any',
55+
String ua = 'any',
56+
int touchPoints = 0,
57+
}) {
58+
expect(
59+
detectOperatingSystem(
60+
overridePlatform: platform,
61+
overrideUserAgent: ua,
62+
overrideMaxTouchPoints: touchPoints,
63+
),
64+
expectedOs);
65+
}
66+
67+
test('Determine unknown for weird values of platform/ua', () {
68+
expectOs(OperatingSystem.unknown);
69+
});
70+
71+
test('Determine MacOS if platform starts by Mac', () {
72+
expectOs(
73+
OperatingSystem.macOs,
74+
platform: 'MacIntel',
75+
);
76+
expectOs(
77+
OperatingSystem.macOs,
78+
platform: 'MacAnythingElse',
79+
);
80+
});
81+
82+
test('Determine iOS if platform contains iPhone/iPad/iPod', () {
83+
expectOs(
84+
OperatingSystem.iOs,
85+
platform: 'iPhone',
86+
);
87+
expectOs(
88+
OperatingSystem.iOs,
89+
platform: 'iPhone Simulator',
90+
);
91+
expectOs(
92+
OperatingSystem.iOs,
93+
platform: 'iPad',
94+
);
95+
expectOs(
96+
OperatingSystem.iOs,
97+
platform: 'iPad Simulator',
98+
);
99+
expectOs(
100+
OperatingSystem.iOs,
101+
platform: 'iPod',
102+
);
103+
expectOs(
104+
OperatingSystem.iOs,
105+
platform: 'iPod Simulator',
106+
);
107+
});
108+
109+
// See https://github.com/flutter/flutter/issues/81918
110+
test('Tell apart MacOS from iOS requesting a desktop site.', () {
111+
expectOs(
112+
OperatingSystem.macOs,
113+
platform: 'MacARM',
114+
);
115+
116+
expectOs(
117+
OperatingSystem.iOs,
118+
platform: 'MacARM',
119+
touchPoints: 5,
120+
);
121+
});
122+
123+
test('Determine Android if user agent contains Android', () {
124+
expectOs(
125+
OperatingSystem.android,
126+
ua: 'Mozilla/5.0 (Linux; U; Android 2.2) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',
127+
);
128+
});
129+
130+
test('Determine Linux if the platform begins with Linux', () {
131+
expectOs(
132+
OperatingSystem.linux,
133+
platform: 'Linux',
134+
);
135+
expectOs(
136+
OperatingSystem.linux,
137+
platform: 'Linux armv8l',
138+
);
139+
expectOs(
140+
OperatingSystem.linux,
141+
platform: 'Linux x86_64',
142+
);
143+
});
144+
145+
test('Determine Windows if the platform begins with Win', () {
146+
expectOs(
147+
OperatingSystem.windows,
148+
platform: 'Windows',
149+
);
150+
expectOs(
151+
OperatingSystem.windows,
152+
platform: 'Win32',
153+
);
154+
expectOs(
155+
OperatingSystem.windows,
156+
platform: 'Win16',
157+
);
158+
expectOs(
159+
OperatingSystem.windows,
160+
platform: 'WinCE',
161+
);
162+
});
47163
});
48164
}

0 commit comments

Comments
 (0)