From afd6b8a8a208ac8c32e9e0a108772849e87985de Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Fri, 18 Jul 2025 15:32:45 -0600 Subject: [PATCH 1/3] Add method to check for WebKit WebView user agent string --- src/lib/index.js | 6 ++++++ src/lib/supports_pixelated_image.js | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lib/index.js b/src/lib/index.js index 816dcab2d31..3368f86477a 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -765,6 +765,12 @@ lib.isIOS = function() { return IS_IOS_REGEX.test(window.navigator.userAgent); }; +// The WKWebView user agent string doesn't include 'Safari', so we need a separate test +// for a UA string like this: +// Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) +const IS_MAC_WKWEBVIEW_REGEX = /Macintosh.+AppleWebKit.+Gecko\)$/; +lib.isMacWKWebView = () => IS_MAC_WKWEBVIEW_REGEX.test(window.navigator.userAgent); + var FIREFOX_VERSION_REGEX = /Firefox\/(\d+)\.\d+/; lib.getFirefoxVersion = function() { var match = FIREFOX_VERSION_REGEX.exec(window.navigator.userAgent); diff --git a/src/lib/supports_pixelated_image.js b/src/lib/supports_pixelated_image.js index 95b3d0feef4..b099a5f715a 100644 --- a/src/lib/supports_pixelated_image.js +++ b/src/lib/supports_pixelated_image.js @@ -19,7 +19,7 @@ function supportsPixelatedImage() { _supportsPixelated = false; // @see https://github.com/plotly/plotly.js/issues/6604 - var unsupportedBrowser = Lib.isSafari() || Lib.isIOS(); + var unsupportedBrowser = Lib.isSafari() || Lib.isMacWKWebView() || Lib.isIOS(); if(window.navigator.userAgent && !unsupportedBrowser) { var declarations = Array.from(constants.CSS_DECLARATIONS).reverse(); From ae0835dbc61f300d4d6e0084374731b49df1a68f Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Fri, 18 Jul 2025 15:33:08 -0600 Subject: [PATCH 2/3] Add user agent related method tests --- test/jasmine/tests/lib_test.js | 61 ++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/test/jasmine/tests/lib_test.js b/test/jasmine/tests/lib_test.js index 11b771fef08..b8cd1bf5981 100644 --- a/test/jasmine/tests/lib_test.js +++ b/test/jasmine/tests/lib_test.js @@ -2956,6 +2956,67 @@ describe('Test lib.js:', function() { }); }); }); + + describe("User agent", () => { + const userAgentStrings = { + iOSSafari: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.4 Mobile/15E148 Safari/604.1", + iOSChrome: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/138.0.7204.156 Mobile/15E148 Safari/604.1", + macChrome: "Mozilla/5.0 (Macintosh; Intel Mac OS X 15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36", + macSafari: "Mozilla/5.0 (Macintosh; Intel Mac OS X 15_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.4 Safari/605.1.15", + macWKWebView: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko)", + winFirefox: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0" + } + + describe('isIOS', () => { + [userAgentStrings.iOSChrome, userAgentStrings.iOSSafari].forEach(uaString => { + it('matches an iOS user agent string', () => { + spyOnProperty(navigator, 'userAgent').and.returnValue(uaString) + expect(Lib.isIOS()).toBe(true) + }) + }) + + it("doesn't match a non-iOS user agent string", () => { + spyOnProperty(navigator, 'userAgent').and.returnValue(userAgentStrings.macSafari) + expect(Lib.isIOS()).toBe(false) + }) + }) + + describe('isSafari', () => { + it('matches a Safari user agent string', () => { + spyOnProperty(navigator, 'userAgent').and.returnValue(userAgentStrings.macSafari) + expect(Lib.isSafari()).toBe(true) + }) + + it("doesn't match a non-Safari user agent string", () => { + spyOnProperty(navigator, 'userAgent').and.returnValue(userAgentStrings.macChrome) + expect(Lib.isSafari()).toBe(false) + }) + }) + + describe('isMacWKWebView', () => { + it('matches a Safari user agent string', () => { + spyOnProperty(navigator, 'userAgent').and.returnValue(userAgentStrings.macWKWebView) + expect(Lib.isMacWKWebView()).toBe(true) + }) + + it("doesn't match a non-Safari user agent string", () => { + spyOnProperty(navigator, 'userAgent').and.returnValue(userAgentStrings.macSafari) + expect(Lib.isMacWKWebView()).toBe(false) + }) + }) + + describe('getFirefoxVersion', () => { + it('gets the Firefox version from the user agent string', () => { + spyOnProperty(navigator, 'userAgent').and.returnValue(userAgentStrings.winFirefox) + expect(Lib.getFirefoxVersion()).toBe(140) + }) + + it("returns null for a non-Firefox user agent string", () => { + spyOnProperty(navigator, 'userAgent').and.returnValue(userAgentStrings.macSafari) + expect(Lib.getFirefoxVersion()).toBe(null) + }) + }) + }) }); describe('Queue', function() { From d927fb62ea52553d858aca6f4d0efc1dfc1ba9dc Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Fri, 18 Jul 2025 15:46:46 -0600 Subject: [PATCH 3/3] Add draftlog --- draftlogs/7479_fix.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 draftlogs/7479_fix.md diff --git a/draftlogs/7479_fix.md b/draftlogs/7479_fix.md new file mode 100644 index 00000000000..6c77da50c99 --- /dev/null +++ b/draftlogs/7479_fix.md @@ -0,0 +1 @@ +- Add method to check for WebKit WebView user agent string [[#7479](https://github.com/plotly/plotly.js/pull/7479)]