Skip to content

Commit 793026a

Browse files
committed
Add workaround for video scaling issue in iOS 17 below 17.4
REDMINE-20640
1 parent 1d98f64 commit 793026a

File tree

8 files changed

+153
-10
lines changed

8 files changed

+153
-10
lines changed

entry_types/scrolled/package/src/frontend/__stories__/browserBugs-stories.js renamed to entry_types/scrolled/package/src/frontend/__stories__/browserBugs/chromeWebaudioCurrentTimeSegfault/stories.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ function Test() {
2222

2323
function setup() {
2424
const audio = document.createElement('audio');
25-
25+
2626
audio.setAttribute('controls', true);
2727
audio.setAttribute('crossorigin', 'anonymous');
2828

@@ -90,4 +90,3 @@ async function run(commands) {
9090
await c;
9191
}
9292
}
93-
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import React from 'react';
2+
3+
import squareVideo from './100x100.mp4';
4+
import landscapeVideo from './200x100.mp4';
5+
6+
export default {
7+
title: 'Frontend/Browser Bugs',
8+
parameters: {
9+
percy: {skip: true}
10+
}
11+
}
12+
13+
export const safariVideoDimension = () => (
14+
<div>
15+
<style>
16+
{`
17+
.wr {width: 300px; height: 300px; }
18+
.wr > div,
19+
.video-js { height: 100%; }
20+
21+
video {
22+
border: solid 1px red;
23+
}
24+
`}`
25+
</style>
26+
<p>
27+
Load video 1, release it, load video 2. Observe the video being scaled
28+
down to only cover parts of the upper part of the video element.
29+
</p>
30+
<div className="wr" id="container"></div>
31+
<button onClick={() => loadVideo(0)}>Load video 1</button>
32+
<button onClick={() => loadVideo(1)}>Load video 2</button>
33+
<button onClick={() => release()}>Release</button>
34+
</div>
35+
);
36+
37+
const srcs = [
38+
landscapeVideo,
39+
squareVideo
40+
];
41+
42+
let video;
43+
44+
function loadVideo(i) {
45+
if (!video) {
46+
video = document.createElement('video');
47+
video.setAttribute('crossorigin', 'anonymous');
48+
video.setAttribute('playsinline', true);
49+
video.setAttribute('loop', true);
50+
video.setAttribute('class', 'video-js wr');
51+
}
52+
53+
video.src = srcs[i];
54+
55+
// Uncomment to fix
56+
// video.load();
57+
58+
document.getElementById('container').appendChild(video);
59+
60+
setTimeout(() => {
61+
video.play();
62+
}, 1000);
63+
}
64+
65+
function release() {
66+
if (video) {
67+
video.pause();
68+
69+
document.getElementById('container').removeChild(video);
70+
}
71+
}

package/spec/frontend/browser/agent_spec.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,4 +231,53 @@ describe('pageflow.browser.Agent', function() {
231231
expect(agent.matchesDesktopChrome({minVersion: 20})).toBe(false);
232232
});
233233
});
234+
235+
describe('#matchesMobileSafari', function() {
236+
it('returns true for Safari on iPhone', function() {
237+
var agent = new Agent(
238+
'Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_1 like Mac OS X) '+
239+
'AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/14A403 Safari/602.1'
240+
);
241+
242+
expect(agent.matchesMobileSafari()).toBe(true);
243+
});
244+
245+
it('returns false for desktop Safari', function() {
246+
var agent = new Agent(
247+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14) ' +
248+
'AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Safari/605.1.15'
249+
);
250+
251+
expect(agent.matchesMobileSafari()).toBe(false);
252+
});
253+
254+
describe('with osVersions option', () => {
255+
it('returns true for matching mobile Safari', () => {
256+
var agent = new Agent(
257+
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_3 like Mac OS X) ' +
258+
'AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3.1 Mobile/15E148 Safari/604.1'
259+
);
260+
261+
expect(agent.matchesMobileSafari({osVersions: ['17.1', '17.2', '17.3']})).toBe(true);
262+
});
263+
264+
it('returns false for other mobile Safari', () => {
265+
var agent = new Agent(
266+
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) ' +
267+
'AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1'
268+
);
269+
270+
expect(agent.matchesMobileSafari({osVersions: ['17.1', '17.2', '17.3']})).toBe(false);
271+
});
272+
273+
it('returns true for matching Chrome on iPhone', () => {
274+
var agent = new Agent(
275+
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) ' +
276+
'AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/123.0.6312.52 Mobile/15E148 Safari/604.1'
277+
);
278+
279+
expect(agent.matchesMobileSafari({osVersions: ['17.4']})).toBe(true);
280+
});
281+
});
282+
});
234283
});

package/src/frontend/browser/Agent.js

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,24 @@ export const Agent = function(userAgent) {
6060
* Returns true on iOS Safari.
6161
* @return {boolean}
6262
*/
63-
matchesMobileSafari: function() {
64-
var matchers = [/iPod/i, /iPad/i, /iPhone/i];
65-
66-
return (matchers.some(function(matcher) {
67-
return userAgent.match(matcher);
68-
}) &&
69-
!window.MSStream) || //IE exclusion from being detected as an iOS device;
70-
matchesiPadSafari13AndAbove();
63+
matchesMobileSafari: function({osVersions} = {}) {
64+
var deviceMatchers = [/iPod/i, /iPad/i, /iPhone/i];
65+
66+
if (osVersions) {
67+
return (
68+
deviceMatchers.some(matcher => userAgent.match(matcher)) &&
69+
osVersions.some(osVersion => userAgent.includes(osVersion.replace('.', '_')))
70+
)
71+
}
72+
else {
73+
return (
74+
matchesiPadSafari13AndAbove() ||
75+
(
76+
deviceMatchers.some(matcher => userAgent.match(matcher)) &&
77+
!window.MSStream // IE exclusion from being detected as an iOS device;
78+
)
79+
);
80+
}
7181
},
7282

7383
/**

package/src/frontend/browser/video.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,12 @@ browser.feature('mse and native hls support', function(has) {
3737
browser.feature('native video player', function(has) {
3838
return has('iphone platform');
3939
});
40+
41+
browser.feature('video scaling bug fixed by load', function(has) {
42+
// When reusing video elements for videos with different
43+
// resolutions, Safari gets confused and scales videos incorrectly -
44+
// drawing them to only cover a part of the element. This appears to
45+
// not happen when the video is loaded or played immediately after
46+
// changing the source. No longer reproducible in iOS 17.4.
47+
return agent.matchesMobileSafari({osVersions: ['17.0', '17.1', '17.2', '17.3']});
48+
});

package/src/frontend/media/media.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {MediaPool, MediaType} from './MediaPool';
22
import BackboneEvents from 'backbone-events-standalone';
33
import {features} from '../features';
4+
import {browser} from '../browser';
45

56
export const media = {
67
playerPool: new MediaPool({
@@ -25,6 +26,10 @@ export const media = {
2526
options.textTrackSources.forEach(track => player.addRemoteTextTrack(track, true));
2627
}
2728

29+
if (browser.has('video scaling bug fixed by load')) {
30+
player.load();
31+
}
32+
2833
return player;
2934
}
3035
},

0 commit comments

Comments
 (0)