Bug 1783069 - Make aspect-ratio work on <video> properly. r=boris,Oriol,layout-reviewers

See https://github.com/w3c/csswg-drafts/issues/7524 for discussion. This
matches what we do for images.

Differential Revision: https://phabricator.services.mozilla.com/D153664
This commit is contained in:
Emilio Cobos Álvarez 2022-08-16 12:20:15 +00:00
parent a5d1294a68
commit c94712abf1
3 changed files with 54 additions and 29 deletions

View File

@ -397,7 +397,7 @@ nscoord nsVideoFrame::GetMinISize(gfxContext* aRenderingContext) {
// therefore must match the function's return value.
DISPLAY_MIN_INLINE_SIZE(this, result);
// This call handles size-containment
nsSize size = GetVideoIntrinsicSize();
nsSize size = GetIntrinsicSize().ToSize().valueOr(nsSize());
result = GetWritingMode().IsVertical() ? size.height : size.width;
return result;
}
@ -424,7 +424,7 @@ AspectRatio nsVideoFrame::GetIntrinsicRatio() const {
return AspectRatio();
}
HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
auto* element = static_cast<HTMLVideoElement*>(GetContent());
if (Maybe<CSSIntSize> size = element->GetVideoSize()) {
return AspectRatio::FromSize(*size);
}
@ -435,14 +435,22 @@ AspectRatio nsVideoFrame::GetIntrinsicRatio() const {
}
}
if (StylePosition()->mAspectRatio.HasRatio()) {
return AspectRatio();
}
return AspectRatio::FromSize(kFallbackIntrinsicSizeInPixels);
}
bool nsVideoFrame::ShouldDisplayPoster() const {
if (!HasVideoElement()) return false;
if (!HasVideoElement()) {
return false;
}
HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
if (element->GetPlayedOrSeeked() && HasVideoData()) return false;
auto* element = static_cast<HTMLVideoElement*>(GetContent());
if (element->GetPlayedOrSeeked() && HasVideoData()) {
return false;
}
nsCOMPtr<nsIImageLoadingContent> imgContent = do_QueryInterface(mPosterImage);
NS_ENSURE_TRUE(imgContent, false);
@ -461,48 +469,53 @@ bool nsVideoFrame::ShouldDisplayPoster() const {
return true;
}
nsSize nsVideoFrame::GetVideoIntrinsicSize() const {
IntrinsicSize nsVideoFrame::GetIntrinsicSize() {
const auto containAxes = StyleDisplay()->GetContainSizeAxes();
const auto isVideo = HasVideoElement();
// Intrinsic size will be given by contain-intrinsic-size if the element is
// size-contained. If both axes have containment, ContainSize() will ignore
// the fallback size argument, so we can just pass 0,0 or whatever.
// size-contained. If both axes have containment, ContainIntrinsicSize() will
// ignore the fallback size argument, so we can just pass no intrinsic size,
// or whatever.
if (containAxes.IsBoth()) {
return containAxes.ContainSize({}, *this);
}
// An audio element with no "controls" attribute, distinguished by the last
// and only child being the control, falls back to 0,0.
if (!isVideo && !mFrames.LastChild()) {
return containAxes.ContainSize({}, *this);
return containAxes.ContainIntrinsicSize({}, *this);
}
if (!isVideo) {
return containAxes.ContainSize(kFallbackIntrinsicSize, *this);
// An audio element with no "controls" attribute, distinguished by the last
// and only child being the control, falls back to no intrinsic size.
if (!mFrames.LastChild()) {
return containAxes.ContainIntrinsicSize({}, *this);
}
return containAxes.ContainIntrinsicSize(
IntrinsicSize(kFallbackIntrinsicSize), *this);
}
HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
auto* element = static_cast<HTMLVideoElement*>(GetContent());
if (Maybe<CSSIntSize> size = element->GetVideoSize()) {
return containAxes.ContainSize(CSSPixel::ToAppUnits(*size), *this);
return containAxes.ContainIntrinsicSize(
IntrinsicSize(CSSPixel::ToAppUnits(*size)), *this);
}
if (ShouldDisplayPoster()) {
if (Maybe<nsSize> imgSize = PosterImageSize()) {
return containAxes.ContainSize(*imgSize, *this);
return containAxes.ContainIntrinsicSize(IntrinsicSize(*imgSize), *this);
}
}
return containAxes.ContainSize(kFallbackIntrinsicSize, *this);
}
IntrinsicSize nsVideoFrame::GetIntrinsicSize() {
return IntrinsicSize(GetVideoIntrinsicSize());
if (StylePosition()->mAspectRatio.HasRatio()) {
return {};
}
return containAxes.ContainIntrinsicSize(IntrinsicSize(kFallbackIntrinsicSize),
*this);
}
void nsVideoFrame::UpdatePosterSource(bool aNotify) {
NS_ASSERTION(HasVideoElement(), "Only call this on <video> elements.");
HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::poster) &&
if (element->HasAttr(nsGkAtoms::poster) &&
!element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::poster,
nsGkAtoms::_empty, eIgnoreCase)) {
nsAutoString posterStr;

View File

@ -47,7 +47,6 @@ class nsVideoFrame final : public nsContainerFrame,
const Maybe<OnNonvisible>& aNonvisibleAction = Nothing()) override;
/* get the size of the video's display */
nsSize GetVideoIntrinsicSize() const;
mozilla::IntrinsicSize GetIntrinsicSize() override;
mozilla::AspectRatio GetIntrinsicRatio() const override;
SizeComputationResult ComputeSize(

View File

@ -39,10 +39,7 @@ t.step(function() {
video.setAttribute("height", "100");
video.src = getVideoURI('/media/2x2-green');
document.body.appendChild(video);
// Videos default to a size of 300x150px and calculate their aspect ratio
// based on that before the video is loaded. So this should be 2, ignoring
// the 2.5 that it would be based on the attributes.
assert_ratio(video, 2);
assert_ratio(video, 2.5);
video.onloadeddata = t.step_func_done(function() {
// When loaded this video is square.
@ -50,6 +47,22 @@ t.step(function() {
});
}, "aspect ratio for regular video");
// Same but with auto width.
t.step(function() {
video = document.createElement("video");
video.setAttribute("width", "250");
video.setAttribute("height", "100");
video.style.width = "auto";
video.src = getVideoURI('/media/2x2-green');
document.body.appendChild(video);
assert_ratio(video, 2.5);
video.onloadeddata = t.step_func_done(function() {
// When loaded this video is square.
assert_ratio(video, 1);
});
}, "aspect ratio for regular video with width: auto and height: auto");
test_computed_style("10", "20", "auto 10 / 20");
test_computed_style("0.5", "1.5", "auto 0.5 / 1.5");
test_computed_style("0", "1", "auto 0 / 1");