Commit 2af326b1 authored by Fredrik Söderqvist's avatar Fredrik Söderqvist Committed by Chromium LUCI CQ

Fix bounding box propagation for <foreignObject>

Since the oBB is in unzoomed space but the <foreignObject>
local-to-parent transform performs adjustment for zoom we need to
perform correction when propagating it.
Similarly change LayoutSVGForeignObject::StrokeBoundingBox() to return
the same bounds as VisualRectInLocalSVGCoordinates() (which is zoomed).

Bug: 1162993
Change-Id: I430cd7832b37475ef1d139997ef5ac5c0bcc17c9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2627491Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Commit-Queue: Fredrik Söderquist <fs@opera.com>
Cr-Commit-Position: refs/heads/master@{#844016}
parent b9a6819e
...@@ -1262,10 +1262,10 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, ...@@ -1262,10 +1262,10 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver,
// SVGGraphicsElement::getBBox(). // SVGGraphicsElement::getBBox().
// NOTE: Markers are not specifically ignored here by SVG 1.1 spec, but we // NOTE: Markers are not specifically ignored here by SVG 1.1 spec, but we
// ignore them since stroke-width is ignored (and marker size can depend on // ignore them since stroke-width is ignored (and marker size can depend on
// stroke-width). objectBoundingBox is returned local coordinates. // stroke-width). objectBoundingBox is returned in local coordinates and
// always unzoomed.
// The name objectBoundingBox is taken from the SVG 1.1 spec. // The name objectBoundingBox is taken from the SVG 1.1 spec.
virtual FloatRect ObjectBoundingBox() const; virtual FloatRect ObjectBoundingBox() const;
virtual FloatRect StrokeBoundingBox() const;
// Returns the smallest rectangle enclosing all of the painted content // Returns the smallest rectangle enclosing all of the painted content
// respecting clipping, masking, filters, opacity, stroke-width and markers. // respecting clipping, masking, filters, opacity, stroke-width and markers.
...@@ -1275,6 +1275,11 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, ...@@ -1275,6 +1275,11 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver,
// coordinate space is the viewport space. // coordinate space is the viewport space.
virtual FloatRect VisualRectInLocalSVGCoordinates() const; virtual FloatRect VisualRectInLocalSVGCoordinates() const;
// Like VisualRectInLocalSVGCoordinates() but does not include visual overflow
// (name is misleading). May be zoomed (currently only for <foreignObject>,
// which represents this via its LocalToSVGParentTransform()).
virtual FloatRect StrokeBoundingBox() const;
// This returns the transform applying to the local SVG coordinate space, // This returns the transform applying to the local SVG coordinate space,
// which combines the CSS transform properties and animation motion transform. // which combines the CSS transform properties and animation motion transform.
// See SVGElement::calculateTransform(). // See SVGElement::calculateTransform().
......
...@@ -72,7 +72,7 @@ class LayoutSVGForeignObject final : public LayoutSVGBlock { ...@@ -72,7 +72,7 @@ class LayoutSVGForeignObject final : public LayoutSVGBlock {
} }
FloatRect StrokeBoundingBox() const override { FloatRect StrokeBoundingBox() const override {
NOT_DESTROYED(); NOT_DESTROYED();
return ObjectBoundingBox(); return VisualRectInLocalSVGCoordinates();
} }
FloatRect VisualRectInLocalSVGCoordinates() const override { FloatRect VisualRectInLocalSVGCoordinates() const override {
NOT_DESTROYED(); NOT_DESTROYED();
......
...@@ -387,4 +387,25 @@ TEST_F(LayoutSVGForeignObjectTest, HitTestUnderScrollingAncestor) { ...@@ -387,4 +387,25 @@ TEST_F(LayoutSVGForeignObjectTest, HitTestUnderScrollingAncestor) {
EXPECT_EQ(PhysicalOffset(450, 450), result.PointInInnerNodeFrame()); EXPECT_EQ(PhysicalOffset(450, 450), result.PointInInnerNodeFrame());
} }
TEST_F(LayoutSVGForeignObjectTest, BBoxPropagationZoomed) {
GetFrame().SetPageZoomFactor(2);
SetBodyInnerHTML(R"HTML(
<svg>
<g>
<foreignObject x="6" y="5" width="100" height="50" id="target"/>
</g>
</svg>
)HTML");
UpdateAllLifecyclePhasesForTest();
const auto& target = *GetLayoutObjectByElementId("target");
ASSERT_EQ(target.StyleRef().EffectiveZoom(), 2);
EXPECT_EQ(target.ObjectBoundingBox(), FloatRect(6, 5, 100, 50));
EXPECT_EQ(target.StrokeBoundingBox(), FloatRect(12, 10, 200, 100));
const auto& parent_g = *target.Parent();
EXPECT_EQ(parent_g.ObjectBoundingBox(), FloatRect(6, 5, 100, 50));
EXPECT_EQ(parent_g.StrokeBoundingBox(), FloatRect(6, 5, 100, 50));
}
} // namespace blink } // namespace blink
...@@ -143,6 +143,16 @@ static bool HasValidBoundingBoxForContainer(const LayoutObject& object) { ...@@ -143,6 +143,16 @@ static bool HasValidBoundingBoxForContainer(const LayoutObject& object) {
return false; return false;
} }
static FloatRect ObjectBoundsForPropagation(const LayoutObject& object) {
FloatRect bounds = object.ObjectBoundingBox();
// The local-to-parent transform for <foreignObject> contains a zoom inverse,
// so we need to apply zoom to the bounding box that we use for propagation to
// be in the correct coordinate space.
if (IsA<LayoutSVGForeignObject>(object))
bounds.Scale(object.StyleRef().EffectiveZoom());
return bounds;
}
bool SVGContentContainer::UpdateBoundingBoxes(bool& object_bounding_box_valid) { bool SVGContentContainer::UpdateBoundingBoxes(bool& object_bounding_box_valid) {
object_bounding_box_valid = false; object_bounding_box_valid = false;
...@@ -154,8 +164,9 @@ bool SVGContentContainer::UpdateBoundingBoxes(bool& object_bounding_box_valid) { ...@@ -154,8 +164,9 @@ bool SVGContentContainer::UpdateBoundingBoxes(bool& object_bounding_box_valid) {
if (!HasValidBoundingBoxForContainer(*current)) if (!HasValidBoundingBoxForContainer(*current))
continue; continue;
const AffineTransform& transform = current->LocalToSVGParentTransform(); const AffineTransform& transform = current->LocalToSVGParentTransform();
UpdateObjectBoundingBox(object_bounding_box, object_bounding_box_valid, UpdateObjectBoundingBox(
transform.MapRect(current->ObjectBoundingBox())); object_bounding_box, object_bounding_box_valid,
transform.MapRect(ObjectBoundsForPropagation(*current)));
stroke_bounding_box.Unite(transform.MapRect(current->StrokeBoundingBox())); stroke_bounding_box.Unite(transform.MapRect(current->StrokeBoundingBox()));
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment