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:
@@ -41,6 +41,9 @@ bool AnimationPerformanceWarning::ToLocalizedString(
|
|||||||
|
|
||||||
return NS_SUCCEEDED(ToLocalizedStringWithIntParams<2>(
|
return NS_SUCCEEDED(ToLocalizedStringWithIntParams<2>(
|
||||||
"CompositorAnimationWarningContentTooLargeArea", aLocalizedString));
|
"CompositorAnimationWarningContentTooLargeArea", aLocalizedString));
|
||||||
|
case Type::NonScalingStroke:
|
||||||
|
key = "CompositorAnimationWarningNonScalingStroke";
|
||||||
|
break;
|
||||||
case Type::TransformSVG:
|
case Type::TransformSVG:
|
||||||
key = "CompositorAnimationWarningTransformSVG";
|
key = "CompositorAnimationWarningTransformSVG";
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ struct AnimationPerformanceWarning {
|
|||||||
None,
|
None,
|
||||||
ContentTooLarge,
|
ContentTooLarge,
|
||||||
ContentTooLargeArea,
|
ContentTooLargeArea,
|
||||||
|
NonScalingStroke,
|
||||||
TransformSVG,
|
TransformSVG,
|
||||||
TransformFrameInactive,
|
TransformFrameInactive,
|
||||||
TransformIsBlockedByImportantRules,
|
TransformIsBlockedByImportantRules,
|
||||||
|
|||||||
@@ -1632,6 +1632,15 @@ bool KeyframeEffect::CanAnimateTransformOnCompositor(
|
|||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1377,6 +1377,27 @@ waitForAllPaints(async () => {
|
|||||||
await ensureElementRemoval(div);
|
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() {
|
add_task(async function no_throttling_animations_in_transformed_parent() {
|
||||||
const div = addDiv(null, { style: 'overflow: scroll;' +
|
const div = addDiv(null, { style: 'overflow: scroll;' +
|
||||||
'transform: translateX(50px);' });
|
'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
|
## (%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
|
## (%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)
|
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,
|
## CompositorAnimationWarningTransformFrameInactive,
|
||||||
## CompositorAnimationWarningOpacityFrameInactive):
|
## CompositorAnimationWarningOpacityFrameInactive):
|
||||||
## 'transform' and 'opacity' mean CSS property names, don't translate it.
|
## '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
|
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
|
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
|
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/ScrollContainerFrame.h"
|
||||||
#include "mozilla/StaticPrefs_browser.h"
|
#include "mozilla/StaticPrefs_browser.h"
|
||||||
#include "mozilla/StaticPrefs_layout.h"
|
#include "mozilla/StaticPrefs_layout.h"
|
||||||
|
#include "mozilla/SVGUtils.h"
|
||||||
#include "mozilla/ToString.h"
|
#include "mozilla/ToString.h"
|
||||||
#include "mozilla/UniquePtr.h"
|
#include "mozilla/UniquePtr.h"
|
||||||
|
|
||||||
@@ -6681,6 +6682,14 @@ static bool EstablishesBFC(const nsBlockFrame* aFrame) {
|
|||||||
|
|
||||||
void nsBlockFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
|
void nsBlockFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
|
||||||
nsContainerFrame::DidSetComputedStyle(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) {
|
if (!aOldStyle) {
|
||||||
return;
|
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.
|
// to update the cached nsTextNode indexes that they correspond to.
|
||||||
FRAME_STATE_BIT(SVG, 24, NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY)
|
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 bits that apply to text frames ==============================
|
||||||
|
|
||||||
FRAME_STATE_GROUP(Text, nsTextFrame)
|
FRAME_STATE_GROUP(Text, nsTextFrame)
|
||||||
|
|||||||
@@ -84,6 +84,11 @@ nsresult SVGGeometryFrame::AttributeChanged(int32_t aNameSpaceID,
|
|||||||
/* virtual */
|
/* virtual */
|
||||||
void SVGGeometryFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
|
void SVGGeometryFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
|
||||||
nsIFrame::DidSetComputedStyle(aOldComputedStyle);
|
nsIFrame::DidSetComputedStyle(aOldComputedStyle);
|
||||||
|
if (StyleSVGReset()->HasNonScalingStroke() &&
|
||||||
|
(!aOldComputedStyle ||
|
||||||
|
!aOldComputedStyle->StyleSVGReset()->HasNonScalingStroke())) {
|
||||||
|
SVGUtils::UpdateNonScalingStrokeStateBit(this);
|
||||||
|
}
|
||||||
auto* element = static_cast<SVGGeometryElement*>(GetContent());
|
auto* element = static_cast<SVGGeometryElement*>(GetContent());
|
||||||
if (!aOldComputedStyle) {
|
if (!aOldComputedStyle) {
|
||||||
element->ClearAnyCachedPath();
|
element->ClearAnyCachedPath();
|
||||||
|
|||||||
@@ -2787,6 +2787,15 @@ void SVGTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||||||
aLists.Content()->AppendNewToTop<DisplaySVGText>(aBuilder, this);
|
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,
|
nsresult SVGTextFrame::AttributeChanged(int32_t aNameSpaceID,
|
||||||
nsAtom* aAttribute, int32_t aModType) {
|
nsAtom* aAttribute, int32_t aModType) {
|
||||||
if (aNameSpaceID != kNameSpaceID_None) {
|
if (aNameSpaceID != kNameSpaceID_None) {
|
||||||
|
|||||||
@@ -199,6 +199,8 @@ class SVGTextFrame final : public SVGDisplayContainerFrame {
|
|||||||
void Init(nsIContent* aContent, nsContainerFrame* aParent,
|
void Init(nsIContent* aContent, nsContainerFrame* aParent,
|
||||||
nsIFrame* aPrevInFlow) override;
|
nsIFrame* aPrevInFlow) override;
|
||||||
|
|
||||||
|
void DidSetComputedStyle(ComputedStyle* aOldComputedStyle) override;
|
||||||
|
|
||||||
nsresult AttributeChanged(int32_t aNamespaceID, nsAtom* aAttribute,
|
nsresult AttributeChanged(int32_t aNamespaceID, nsAtom* aAttribute,
|
||||||
int32_t aModType) override;
|
int32_t aModType) override;
|
||||||
|
|
||||||
|
|||||||
@@ -1071,6 +1071,21 @@ bool SVGUtils::GetNonScalingStrokeTransform(const nsIFrame* aFrame,
|
|||||||
return aUserToOuterSVG->HasNonTranslation() && !aUserToOuterSVG->IsSingular();
|
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
|
// The logic here comes from _cairo_stroke_style_max_distance_from_path
|
||||||
static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
|
static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
|
||||||
const nsIFrame* aFrame,
|
const nsIFrame* aFrame,
|
||||||
|
|||||||
@@ -422,6 +422,12 @@ class SVGUtils final {
|
|||||||
static bool GetNonScalingStrokeTransform(const nsIFrame* aFrame,
|
static bool GetNonScalingStrokeTransform(const nsIFrame* aFrame,
|
||||||
gfxMatrix* aUserToOuterSVG);
|
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
|
* 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.
|
* the path's device space path extents, its stroke style and its ctm.
|
||||||
|
|||||||
Reference in New Issue
Block a user