Skip to content

Commit

Permalink
Merge pull request #79 from Workiva/FED-2880_fix-edge-detection
Browse files Browse the repository at this point in the history
FED-2880 Fix Blink-based Microsoft Edge Browser Detection
  • Loading branch information
rm-astro-wf authored Jun 28, 2024
2 parents 945e9df + c5e9fc6 commit c09ccbd
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 4 deletions.
1 change: 1 addition & 0 deletions .github/workflows/dart_ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ jobs:
- name: Run tests
run: dart test
- uses: anchore/sbom-action@v0
if: ${{ matrix.sdk == '2.19.6' }}
with:
path: ./
format: cyclonedx-json
53 changes: 49 additions & 4 deletions lib/src/browser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,14 @@ class Browser {
firefox,
safari,
wkWebView,
chrome,
edgeChrome,
chrome
];

bool get isChrome => this == chrome;
bool get isChrome => this == chrome || this == edgeChrome;

/// Whether the browser is [edgeChrome].
bool get isEdgeChrome => this == edgeChrome;
bool get isFirefox => this == firefox;
bool get isSafari => this == safari;
bool get isInternetExplorer => this == internetExplorer;
Expand All @@ -67,14 +71,27 @@ Browser safari = _Safari();
Browser internetExplorer = _InternetExplorer();
Browser wkWebView = _WKWebView();

/// The Edge browser from Microsoft that is based on the Blink rendering engine.
///
/// * For the purposes of detecting capabilities / features - this browser should
/// be treated as a Blink/Chrome browser.
/// *(Truthy for both [Browser.isChrome] and [Browser.isEdgeChrome])*
/// * For the purposes of system logging, it should be detected as a standalone
/// product with its own version.
///
/// > **NOTE** In addition to the Edge `version`, the underlying Blink engine
/// version can be accessed via `chromeVersion`.
EdgeChrome edgeChrome = EdgeChrome._();

class _Chrome extends Browser {
_Chrome() : super('Chrome', _isChrome, _getVersion);

static bool _isChrome(NavigatorProvider navigator) =>
navigator.vendor.contains('Google');

static Version _getVersion(NavigatorProvider navigator) {
Match? match = RegExp(r"Chrome/(\d+)\.(\d+)\.(\d+)\.(\d+)\s")
static Version _getVersion(NavigatorProvider navigator,
{String namePrefix = 'Chrome'}) {
Match? match = RegExp(namePrefix + r"/(\d+)\.(\d+)\.(\d+)\.(\d+)")
.firstMatch(navigator.appVersion);
if (match != null) {
var major = int.parse(match.group(1)!);
Expand All @@ -88,6 +105,34 @@ class _Chrome extends Browser {
}
}

/// See: [edgeChrome]
@visibleForTesting
@internal
class EdgeChrome extends Browser {
EdgeChrome._()
: super(
// name should be `Edge` (essentially for logging purposes only)
'Edge',
_isEdge,
_getVersion,
// className should remain chrome since the rendering engine is the same
className: 'chrome',
);

/// See: <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent#microsoft_edge_ua_string>
static bool _isEdge(NavigatorProvider navigator) =>
navigator.userAgent.contains('Edg/');

static Version _getVersion(NavigatorProvider navigator) =>
_Chrome._getVersion(Browser.navigator ?? TestNavigator(),
namePrefix: 'Edg');

/// The underlying Blink rendering engine version that this version of Edge uses.
Version get chromeVersion => _chromeVersion ??=
_Chrome._getVersion(Browser.navigator ?? TestNavigator());
Version? _chromeVersion;
}

class _Firefox extends Browser {
_Firefox() : super('Firefox', _isFirefox, _getVersion);

Expand Down
45 changes: 45 additions & 0 deletions test/browser_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ void main() {
expect(browser.name, Browser.UnknownBrowser.name);
expect(browser.version, Browser.UnknownBrowser.version);
expect(browser.isChrome, false);
expect(browser.isEdgeChrome, isFalse);
expect(browser.isFirefox, false);
expect(browser.isSafari, false);
expect(browser.isInternetExplorer, false);
Expand All @@ -31,6 +32,7 @@ void main() {
expect(browser.name, 'Fake');
expect(browser.version, Version(1, 1, 0));
expect(browser.isChrome, false);
expect(browser.isEdgeChrome, isFalse);
expect(browser.isFirefox, false);
expect(browser.isSafari, false);
expect(browser.isInternetExplorer, false);
Expand All @@ -42,18 +44,42 @@ void main() {

expect(browser.name, 'Chrome');
expect(browser.isChrome, true);
expect(browser.isEdgeChrome, isFalse);
expect(browser.isFirefox, false);
expect(browser.isSafari, false);
expect(browser.isInternetExplorer, false);
expect(browser.version, Version(53, 0, 2785, build: '143'));
});

test('Edge Chrome', () {
Browser.navigator = testChromeEdge();
browser = Browser.getCurrentBrowser();

expect(browser.name, 'Edge');
expect([browser.isChrome, browser.isEdgeChrome], everyElement(isTrue),
reason:
'Blink-based Edge browser should be detected as both Chrome and Edge '
'since feature support is based on Chrome, but should identify as Edge '
'for logging purposes');
expect(browser.isFirefox, isFalse);
expect(browser.isSafari, isFalse);
expect(browser.isInternetExplorer, isFalse);
expect(browser.version, Version(91, 0, 864, build: '59'));

expect(browser, isA<EdgeChrome>());
expect(
(browser as EdgeChrome).chromeVersion,
Version(91, 0, 4472, build: '124'),
);
});

test('Chromeless', () {
Browser.navigator = testChromeless();
browser = Browser.getCurrentBrowser();

expect(browser.name, 'Chrome');
expect(browser.isChrome, true);
expect(browser.isEdgeChrome, isFalse);
expect(browser.isFirefox, false);
expect(browser.isSafari, false);
expect(browser.isInternetExplorer, false);
Expand All @@ -66,18 +92,33 @@ void main() {

expect(browser.name, 'Internet Explorer');
expect(browser.isChrome, false);
expect(browser.isEdgeChrome, isFalse);
expect(browser.isFirefox, false);
expect(browser.isSafari, false);
expect(browser.isInternetExplorer, true);
expect(browser.version, Version(11, 0, 0));
});

test('Internet Explorer (Edge)', () {
Browser.navigator = testInternetExplorerEdge();
browser = Browser.getCurrentBrowser();

expect(browser.name, 'Internet Explorer');
expect(browser.isChrome, isFalse);
expect(browser.isEdgeChrome, isFalse);
expect(browser.isFirefox, isFalse);
expect(browser.isSafari, isFalse);
expect(browser.isInternetExplorer, isTrue);
expect(browser.version, Version(12, 10136, 0));
});

test('Firefox', () {
Browser.navigator = testFirefox();
browser = Browser.getCurrentBrowser();

expect(browser.name, 'Firefox');
expect(browser.isChrome, false);
expect(browser.isEdgeChrome, isFalse);
expect(browser.isFirefox, true);
expect(browser.isSafari, false);
expect(browser.isInternetExplorer, false);
Expand All @@ -95,6 +136,7 @@ void main() {

expect(browser.name, 'Safari');
expect(browser.isChrome, false);
expect(browser.isEdgeChrome, isFalse);
expect(browser.isFirefox, false);
expect(browser.isSafari, true);
expect(browser.isInternetExplorer, false);
Expand All @@ -110,6 +152,7 @@ void main() {

expect(browser.name, 'Safari');
expect(browser.isChrome, false);
expect(browser.isEdgeChrome, isFalse);
expect(browser.isFirefox, false);
expect(browser.isSafari, true);
expect(browser.isInternetExplorer, false);
Expand All @@ -125,6 +168,7 @@ void main() {

expect(browser.name, 'Safari');
expect(browser.isChrome, false);
expect(browser.isEdgeChrome, isFalse);
expect(browser.isFirefox, false);
expect(browser.isSafari, true);
expect(browser.isInternetExplorer, false);
Expand All @@ -138,6 +182,7 @@ void main() {

expect(browser.name, 'WKWebView');
expect(browser.isChrome, false);
expect(browser.isEdgeChrome, isFalse);
expect(browser.isFirefox, false);
expect(browser.isSafari, false);
expect(browser.isWKWebView, true);
Expand Down
21 changes: 21 additions & 0 deletions test/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ const String ieAppVersionTestString =
const String ieAppNameTestString = 'Netscape';
const String ieVendorTestString = 'Microsoft';

const String ieEdgeUserAgentTestString =
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10136';
const String ieEdgeAppVersionTestString =
'5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10136';

/// See: <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent#microsoft_edge_ua_string>
const String chromeEdgeUserAgentTestString =
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.59';
const String chromeEdgeAppVersionTestString =
'5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.59';

const String safariUserAgentTestString =
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/601.7.8 (KHTML, like Gecko) Version/9.1.3 Safari/601.7.8';
const String safariAppVersionTestString =
Expand Down Expand Up @@ -58,6 +69,11 @@ TestNavigator testChrome({
..vendor = vendor;
}

TestNavigator testChromeEdge() => testChrome(
userAgent: chromeEdgeUserAgentTestString,
appVersion: chromeEdgeAppVersionTestString,
);

TestNavigator testChromeless({
String userAgent = chromelessUserAgentTestString,
String appVersion = chromelessAppVersionTestString,
Expand Down Expand Up @@ -97,6 +113,11 @@ TestNavigator testInternetExplorer({
..vendor = vendor;
}

TestNavigator testInternetExplorerEdge() => testInternetExplorer(
userAgent: ieEdgeUserAgentTestString,
appVersion: ieEdgeAppVersionTestString,
);

TestNavigator testSafari({
String userAgent = safariUserAgentTestString,
String appVersion = safariAppVersionTestString,
Expand Down

0 comments on commit c09ccbd

Please sign in to comment.