Bug 1734476 - Don't run on compositor when content may contain non-scaling-stroke r=emilio

Differential Revision: https://phabricator.services.mozilla.com/D227400
This commit is contained in:
longsonr 2024-11-07 11:03:46 +00:00
Родитель d8a80fdff3
Коммит 15674fd75a
12 изменённых файлов: 87 добавлений и 1 удалений

Просмотреть файл

@ -41,6 +41,9 @@ bool AnimationPerformanceWarning::ToLocalizedString(
return NS_SUCCEEDED(ToLocalizedStringWithIntParams<2>(
"CompositorAnimationWarningContentTooLargeArea", aLocalizedString));
case Type::NonScalingStroke:
key = "CompositorAnimationWarningNonScalingStroke";
break;
case Type::TransformSVG:
key = "CompositorAnimationWarningTransformSVG";
break;

Просмотреть файл

@ -21,6 +21,7 @@ struct AnimationPerformanceWarning {
None,
ContentTooLarge,
ContentTooLargeArea,
NonScalingStroke,
TransformSVG,
TransformFrameInactive,
TransformIsBlockedByImportantRules,

Просмотреть файл

@ -1632,6 +1632,15 @@ bool KeyframeEffect::CanAnimateTransformOnCompositor(
return false;
}
// If there's any content that might have non-scaling stroke then we can't
// run in the compositor.
if (primaryFrame->IsSVGFrame() &&
primaryFrame->HasAnyStateBits(
NS_STATE_SVG_MAY_CONTAIN_NON_SCALING_STROKE)) {
aPerformanceWarning = AnimationPerformanceWarning::Type::NonScalingStroke;
return false;
}
return true;
}

Просмотреть файл

@ -1377,6 +1377,27 @@ waitForAllPaints(async () => {
await ensureElementRemoval(div);
});
add_task_if_omta_enabled(async function svg_non_scaling_stroke_animation() {
const div = addDiv(null, { style: 'overflow: scroll;' +
'height: 100px; width: 100px;' });
const svg = addSVGElement(div, 'svg', { viewBox: '0 0 250 250',
width: '40px',
height: '40px' });
const rect = addSVGElement(svg, 'rect', { x: '0',
y: '0',
width: '250',
height: '250',
fill: 'red',
style: 'vector-effect: non-scaling-stroke; animation: rotate 100s infinite;'});
const animation = rect.getAnimations()[0];
await waitForAnimationReadyToRestyle(animation);
ok(!SpecialPowers.wrap(animation).isRunningOnCompositor,
'The animation of a non-scaling-stroke element is not running on the compositor');
await ensureElementRemoval(div);
});
add_task(async function no_throttling_animations_in_transformed_parent() {
const div = addDiv(null, { style: 'overflow: scroll;' +
'transform: translateX(50px);' });

Просмотреть файл

@ -19,10 +19,12 @@ CompositorAnimationWarningContentTooLargeArea=Animation cannot be run on the com
## (%3$S, %4$S) is a pair of integer values of a limit based on the viewport size
## (%5$S, %6$S) is a pair of integer values of an absolute limit
CompositorAnimationWarningContentTooLarge2=Animation cannot be run on the compositor because the frame size (%1$S, %2$S) is too large relative to the viewport (larger than (%3$S, %4$S)) or larger than the maximum allowed value (%5$S, %6$S)
## LOCALIZATION NOTE(CompositorAnimationWarningTransformSVG,
## LOCALIZATION NOTE(CompositorAnimationWarningNonScalingStroke,
## CompositorAnimationWarningTransformSVG,
## CompositorAnimationWarningTransformFrameInactive,
## CompositorAnimationWarningOpacityFrameInactive):
## 'transform' and 'opacity' mean CSS property names, don't translate it.
CompositorAnimationWarningNonScalingStroke=Animations of transform on content containing non-scaling-stroke elements cannot be run on the compositor
CompositorAnimationWarningTransformSVG=Animations of transform on elements with SVG transforms cannot be run on the compositor
CompositorAnimationWarningTransformFrameInactive=Animation cannot be run on the compositor because the frame was not marked active for transform animation
CompositorAnimationWarningTransformIsBlockedByImportantRules=Transform animation cannot be run on the compositor because transform-related properties are overridden by !important rules

Просмотреть файл

@ -22,6 +22,7 @@
#include "mozilla/ScrollContainerFrame.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/SVGUtils.h"
#include "mozilla/ToString.h"
#include "mozilla/UniquePtr.h"
@ -6681,6 +6682,14 @@ static bool EstablishesBFC(const nsBlockFrame* aFrame) {
void nsBlockFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
nsContainerFrame::DidSetComputedStyle(aOldStyle);
if (IsInSVGTextSubtree() &&
(StyleSVGReset()->HasNonScalingStroke() &&
(!aOldStyle || !aOldStyle->StyleSVGReset()->HasNonScalingStroke()))) {
nsIFrame* textFrame =
nsLayoutUtils::GetClosestFrameOfType(this, LayoutFrameType::SVGText);
MOZ_ASSERT(textFrame, "Expecting to find an SVG text frame");
SVGUtils::UpdateNonScalingStrokeStateBit(textFrame);
}
if (!aOldStyle) {
return;
}

Просмотреть файл

@ -437,6 +437,10 @@ FRAME_STATE_BIT(SVG, 23, NS_STATE_SVG_TEXT_IN_REFLOW)
// to update the cached nsTextNode indexes that they correspond to.
FRAME_STATE_BIT(SVG, 24, NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY)
// Set on svg frames when they or their descendants may contain non-scaling
// stroke contents.
FRAME_STATE_BIT(SVG, 25, NS_STATE_SVG_MAY_CONTAIN_NON_SCALING_STROKE)
// == Frame state bits that apply to text frames ==============================
FRAME_STATE_GROUP(Text, nsTextFrame)

Просмотреть файл

@ -84,6 +84,11 @@ nsresult SVGGeometryFrame::AttributeChanged(int32_t aNameSpaceID,
/* virtual */
void SVGGeometryFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
nsIFrame::DidSetComputedStyle(aOldComputedStyle);
if (StyleSVGReset()->HasNonScalingStroke() &&
(!aOldComputedStyle ||
!aOldComputedStyle->StyleSVGReset()->HasNonScalingStroke())) {
SVGUtils::UpdateNonScalingStrokeStateBit(this);
}
auto* element = static_cast<SVGGeometryElement*>(GetContent());
if (!aOldComputedStyle) {
element->ClearAnyCachedPath();

Просмотреть файл

@ -2787,6 +2787,15 @@ void SVGTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
aLists.Content()->AppendNewToTop<DisplaySVGText>(aBuilder, this);
}
void SVGTextFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
SVGDisplayContainerFrame::DidSetComputedStyle(aOldComputedStyle);
if (StyleSVGReset()->HasNonScalingStroke() &&
(!aOldComputedStyle ||
!aOldComputedStyle->StyleSVGReset()->HasNonScalingStroke())) {
SVGUtils::UpdateNonScalingStrokeStateBit(this);
}
}
nsresult SVGTextFrame::AttributeChanged(int32_t aNameSpaceID,
nsAtom* aAttribute, int32_t aModType) {
if (aNameSpaceID != kNameSpaceID_None) {

Просмотреть файл

@ -199,6 +199,8 @@ class SVGTextFrame final : public SVGDisplayContainerFrame {
void Init(nsIContent* aContent, nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) override;
void DidSetComputedStyle(ComputedStyle* aOldComputedStyle) override;
nsresult AttributeChanged(int32_t aNamespaceID, nsAtom* aAttribute,
int32_t aModType) override;

Просмотреть файл

@ -1071,6 +1071,21 @@ bool SVGUtils::GetNonScalingStrokeTransform(const nsIFrame* aFrame,
return aUserToOuterSVG->HasNonTranslation() && !aUserToOuterSVG->IsSingular();
}
void SVGUtils::UpdateNonScalingStrokeStateBit(nsIFrame* aFrame) {
MOZ_ASSERT(aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT),
"Called on invalid frame type");
MOZ_ASSERT(aFrame->StyleSVGReset()->HasNonScalingStroke(),
"Expecting initial frame to have non-scaling-stroke style");
do {
MOZ_ASSERT(aFrame->IsSVGFrame(), "Unexpected frame type");
aFrame->AddStateBits(NS_STATE_SVG_MAY_CONTAIN_NON_SCALING_STROKE);
if (aFrame->IsSVGOuterSVGFrame()) {
return;
}
} while (aFrame = aFrame->GetParent());
}
// The logic here comes from _cairo_stroke_style_max_distance_from_path
static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
const nsIFrame* aFrame,

Просмотреть файл

@ -422,6 +422,12 @@ class SVGUtils final {
static bool GetNonScalingStrokeTransform(const nsIFrame* aFrame,
gfxMatrix* aUserToOuterSVG);
/**
* We need to track whether content has non-scaling-stroke because we can't
* asynchronously animate it with a scaling transform.
*/
static void UpdateNonScalingStrokeStateBit(nsIFrame* aFrame);
/**
* Compute the maximum possible device space stroke extents of a path given
* the path's device space path extents, its stroke style and its ctm.