From 0481fa14b411be4899366a19b9a142f648826977 Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Wed, 14 Aug 2019 15:20:20 +0200 Subject: Issue #438: Make the nsSVGOuterSVGAnonChildFrame a reference frame. This causes transforms of the contents to be unaffected by scrolling / offset changes of the element. --- layout/svg/nsSVGOuterSVGFrame.cpp | 56 +++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 23 deletions(-) (limited to 'layout') diff --git a/layout/svg/nsSVGOuterSVGFrame.cpp b/layout/svg/nsSVGOuterSVGFrame.cpp index 434676010..b1ee54eb9 100644 --- a/layout/svg/nsSVGOuterSVGFrame.cpp +++ b/layout/svg/nsSVGOuterSVGFrame.cpp @@ -979,46 +979,56 @@ nsSVGOuterSVGAnonChildFrame::GetType() const return nsGkAtoms::svgOuterSVGAnonChildFrame; } -bool -nsSVGOuterSVGAnonChildFrame::IsSVGTransformed(Matrix* aOwnTransform, - Matrix* aFromParentTransform) const +static Matrix +ComputeOuterSVGAnonChildFrameTransform(const nsSVGOuterSVGAnonChildFrame* aFrame) { // Our elements 'transform' attribute is applied to our nsSVGOuterSVGFrame // parent, and the element's children-only transforms are applied to us, the // anonymous child frame. Since we are the child frame, we apply the // children-only transforms as if they are our own transform. - SVGSVGElement* content = static_cast(mContent); + SVGSVGElement* content = static_cast(aFrame->GetContent()); if (!content->HasChildrenOnlyTransform()) { - return false; + return Matrix(); } // Outer- doesn't use x/y, so we can pass eChildToUserSpace here. gfxMatrix ownMatrix = content->PrependLocalTransformsTo(gfxMatrix(), eChildToUserSpace); - if (ownMatrix.IsIdentity()) { - return false; + if (ownMatrix.HasNonTranslation()) { + // viewBox, currentScale and currentTranslate should only produce a + // rectilinear transform. + MOZ_ASSERT(ownMatrix.IsRectilinear(), + "Non-rectilinear transform will break the following logic"); + + // The nsDisplayTransform code will apply this transform to our frame, + // including to our frame position. We don't want our frame position to + // be scaled though, so we need to correct for that in the transform. + CSSPoint pos = CSSPixel::FromAppUnits(aFrame->GetPosition()); + CSSPoint scaledPos = CSSPoint(ownMatrix._11 * pos.x, ownMatrix._22 * pos.y); + CSSPoint deltaPos = scaledPos - pos; + ownMatrix *= gfxMatrix::Translation(-deltaPos.x, -deltaPos.y); } - if (aOwnTransform) { - if (ownMatrix.HasNonTranslation()) { - // Note: viewBox, currentScale and currentTranslate should only - // produce a rectilinear transform. - MOZ_ASSERT(ownMatrix.IsRectilinear(), - "Non-rectilinear transform will break the following logic"); - - // The nsDisplayTransform code will apply this transform to our frame, - // including to our frame position. We don't want our frame position to - // be scaled though, so we need to correct for that in the transform. - CSSPoint pos = CSSPixel::FromAppUnits(GetPosition()); - CSSPoint scaledPos = CSSPoint(ownMatrix._11 * pos.x, ownMatrix._22 * pos.y); - CSSPoint deltaPos = scaledPos - pos; - ownMatrix *= gfxMatrix::Translation(-deltaPos.x, -deltaPos.y); - } + return gfx::ToMatrix(ownMatrix); +} - *aOwnTransform = gfx::ToMatrix(ownMatrix); +// We want this frame to be a reference frame. An easy way to achieve that is +// to always return true from this method, even for identity transforms. +// This frame being a reference frame ensures that the offset between this +// element and the parent reference frame is completely absorbed by the +// nsDisplayTransform that's created for this frame, and that this offset does +// not affect our descendants' transforms. Consequently, if the element +// moves, e.g. during scrolling, the transform matrices of our contents are +// unaffected. This simplifies invalidation. +bool +nsSVGOuterSVGAnonChildFrame::IsSVGTransformed(Matrix* aOwnTransform, + Matrix* aFromParentTransform) const +{ + if (aOwnTransform) { + *aOwnTransform = ComputeOuterSVGAnonChildFrameTransform(this); } return true; -- cgit v1.2.3