From 793026ad57139130940d704ccd646254b0e4fd56 Mon Sep 17 00:00:00 2001 From: Tim Fischbach Date: Mon, 18 Mar 2024 10:47:20 +0100 Subject: [PATCH] Add workaround for video scaling issue in iOS 17 below 17.4 REDMINE-20640 --- .../stories.js} | 3 +- .../safariVideoDimension/100x100.mp4 | Bin 0 -> 4916 bytes .../safariVideoDimension/200x100.mp4 | Bin 0 -> 5044 bytes .../safariVideoDimension/stories.js | 71 ++++++++++++++++++ package/spec/frontend/browser/agent_spec.js | 49 ++++++++++++ package/src/frontend/browser/Agent.js | 26 +++++-- package/src/frontend/browser/video.js | 9 +++ package/src/frontend/media/media.js | 5 ++ 8 files changed, 153 insertions(+), 10 deletions(-) rename entry_types/scrolled/package/src/frontend/__stories__/{browserBugs-stories.js => browserBugs/chromeWebaudioCurrentTimeSegfault/stories.js} (99%) create mode 100644 entry_types/scrolled/package/src/frontend/__stories__/browserBugs/safariVideoDimension/100x100.mp4 create mode 100644 entry_types/scrolled/package/src/frontend/__stories__/browserBugs/safariVideoDimension/200x100.mp4 create mode 100644 entry_types/scrolled/package/src/frontend/__stories__/browserBugs/safariVideoDimension/stories.js diff --git a/entry_types/scrolled/package/src/frontend/__stories__/browserBugs-stories.js b/entry_types/scrolled/package/src/frontend/__stories__/browserBugs/chromeWebaudioCurrentTimeSegfault/stories.js similarity index 99% rename from entry_types/scrolled/package/src/frontend/__stories__/browserBugs-stories.js rename to entry_types/scrolled/package/src/frontend/__stories__/browserBugs/chromeWebaudioCurrentTimeSegfault/stories.js index 5efd53b74e..99dc4432cb 100644 --- a/entry_types/scrolled/package/src/frontend/__stories__/browserBugs-stories.js +++ b/entry_types/scrolled/package/src/frontend/__stories__/browserBugs/chromeWebaudioCurrentTimeSegfault/stories.js @@ -22,7 +22,7 @@ function Test() { function setup() { const audio = document.createElement('audio'); - + audio.setAttribute('controls', true); audio.setAttribute('crossorigin', 'anonymous'); @@ -90,4 +90,3 @@ async function run(commands) { await c; } } - diff --git a/entry_types/scrolled/package/src/frontend/__stories__/browserBugs/safariVideoDimension/100x100.mp4 b/entry_types/scrolled/package/src/frontend/__stories__/browserBugs/safariVideoDimension/100x100.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..9ff1cb131449527106416d71b9b769bf347065cf GIT binary patch literal 4916 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 ( +
+ +

+ Load video 1, release it, load video 2. Observe the video being scaled + down to only cover parts of the upper part of the video element. +

+
+ + + +
+); + +const srcs = [ + landscapeVideo, + squareVideo +]; + +let video; + +function loadVideo(i) { + if (!video) { + video = document.createElement('video'); + video.setAttribute('crossorigin', 'anonymous'); + video.setAttribute('playsinline', true); + video.setAttribute('loop', true); + video.setAttribute('class', 'video-js wr'); + } + + video.src = srcs[i]; + + // Uncomment to fix + // video.load(); + + document.getElementById('container').appendChild(video); + + setTimeout(() => { + video.play(); + }, 1000); +} + +function release() { + if (video) { + video.pause(); + + document.getElementById('container').removeChild(video); + } +} diff --git a/package/spec/frontend/browser/agent_spec.js b/package/spec/frontend/browser/agent_spec.js index 472024a8b3..f9ebc3ce85 100644 --- a/package/spec/frontend/browser/agent_spec.js +++ b/package/spec/frontend/browser/agent_spec.js @@ -231,4 +231,53 @@ describe('pageflow.browser.Agent', function() { expect(agent.matchesDesktopChrome({minVersion: 20})).toBe(false); }); }); + + describe('#matchesMobileSafari', function() { + it('returns true for Safari on iPhone', function() { + var agent = new Agent( + 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_1 like Mac OS X) '+ + 'AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/14A403 Safari/602.1' + ); + + expect(agent.matchesMobileSafari()).toBe(true); + }); + + it('returns false for desktop Safari', function() { + var agent = new Agent( + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14) ' + + 'AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Safari/605.1.15' + ); + + expect(agent.matchesMobileSafari()).toBe(false); + }); + + describe('with osVersions option', () => { + it('returns true for matching mobile Safari', () => { + var agent = new Agent( + 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_3 like Mac OS X) ' + + 'AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3.1 Mobile/15E148 Safari/604.1' + ); + + expect(agent.matchesMobileSafari({osVersions: ['17.1', '17.2', '17.3']})).toBe(true); + }); + + it('returns false for other mobile Safari', () => { + var agent = new Agent( + 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) ' + + 'AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1' + ); + + expect(agent.matchesMobileSafari({osVersions: ['17.1', '17.2', '17.3']})).toBe(false); + }); + + it('returns true for matching Chrome on iPhone', () => { + var agent = new Agent( + 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) ' + + 'AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/123.0.6312.52 Mobile/15E148 Safari/604.1' + ); + + expect(agent.matchesMobileSafari({osVersions: ['17.4']})).toBe(true); + }); + }); + }); }); diff --git a/package/src/frontend/browser/Agent.js b/package/src/frontend/browser/Agent.js index d6d998746c..d2251e52fc 100644 --- a/package/src/frontend/browser/Agent.js +++ b/package/src/frontend/browser/Agent.js @@ -60,14 +60,24 @@ export const Agent = function(userAgent) { * Returns true on iOS Safari. * @return {boolean} */ - matchesMobileSafari: function() { - var matchers = [/iPod/i, /iPad/i, /iPhone/i]; - - return (matchers.some(function(matcher) { - return userAgent.match(matcher); - }) && - !window.MSStream) || //IE exclusion from being detected as an iOS device; - matchesiPadSafari13AndAbove(); + matchesMobileSafari: function({osVersions} = {}) { + var deviceMatchers = [/iPod/i, /iPad/i, /iPhone/i]; + + if (osVersions) { + return ( + deviceMatchers.some(matcher => userAgent.match(matcher)) && + osVersions.some(osVersion => userAgent.includes(osVersion.replace('.', '_'))) + ) + } + else { + return ( + matchesiPadSafari13AndAbove() || + ( + deviceMatchers.some(matcher => userAgent.match(matcher)) && + !window.MSStream // IE exclusion from being detected as an iOS device; + ) + ); + } }, /** diff --git a/package/src/frontend/browser/video.js b/package/src/frontend/browser/video.js index 0b524b8014..584a865808 100644 --- a/package/src/frontend/browser/video.js +++ b/package/src/frontend/browser/video.js @@ -37,3 +37,12 @@ browser.feature('mse and native hls support', function(has) { browser.feature('native video player', function(has) { return has('iphone platform'); }); + +browser.feature('video scaling bug fixed by load', function(has) { + // When reusing video elements for videos with different + // resolutions, Safari gets confused and scales videos incorrectly - + // drawing them to only cover a part of the element. This appears to + // not happen when the video is loaded or played immediately after + // changing the source. No longer reproducible in iOS 17.4. + return agent.matchesMobileSafari({osVersions: ['17.0', '17.1', '17.2', '17.3']}); +}); diff --git a/package/src/frontend/media/media.js b/package/src/frontend/media/media.js index 320084c6a8..e958759131 100644 --- a/package/src/frontend/media/media.js +++ b/package/src/frontend/media/media.js @@ -1,6 +1,7 @@ import {MediaPool, MediaType} from './MediaPool'; import BackboneEvents from 'backbone-events-standalone'; import {features} from '../features'; +import {browser} from '../browser'; export const media = { playerPool: new MediaPool({ @@ -25,6 +26,10 @@ export const media = { options.textTrackSources.forEach(track => player.addRemoteTextTrack(track, true)); } + if (browser.has('video scaling bug fixed by load')) { + player.load(); + } + return player; } },