Bug 923193. Make transform-origin on SVG elements use the SVG bbox as the reference rectangle. r=heycam

--HG--
extra : rebase_source : f4f620acc51e14625f45b8022385d8a1112fd086
This commit is contained in:
Robert O'Callahan 2013-10-03 08:34:24 -04:00
parent da3a33ddb6
commit 82d7a2bd79
10 changed files with 91 additions and 40 deletions

View File

@ -3829,8 +3829,9 @@ nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame)
NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!");
if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
// TODO: SVG needs to define what percentage translations resolve against.
return nsRect();
gfxRect bbox = nsSVGUtils::GetBBox(const_cast<nsIFrame*>(aFrame));
return nsLayoutUtils::RoundGfxRectToAppRect(bbox,
aFrame->PresContext()->AppUnitsPerCSSPixel()) - aFrame->GetPosition();
}
return nsRect(nsPoint(0, 0), aFrame->GetSize());
@ -3846,8 +3847,9 @@ nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame)
nsRect result;
if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
// TODO: SVG needs to define what percentage translations resolve against.
return result;
gfxRect bbox = nsSVGUtils::GetBBox(const_cast<nsIFrame*>(aFrame));
return nsLayoutUtils::RoundGfxRectToAppRect(bbox,
aFrame->PresContext()->AppUnitsPerCSSPixel()) - aFrame->GetPosition();
}
/* Iterate through the continuation list, unioning together all the
@ -3921,47 +3923,45 @@ nsDisplayTransform::GetDeltaToMozTransformOrigin(const nsIFrame* aFrame,
nsDisplayTransform::GetFrameBoundsForTransform(aFrame));
/* Allows us to access named variables by index. */
float coords[3];
const nscoord* dimensions[2] =
{&boundingRect.width, &boundingRect.height};
float coords[2];
nscoord boundingOffsets[2] = {boundingRect.x, boundingRect.y};
nscoord boundingDimensions[2] = {boundingRect.width, boundingRect.height};
nscoord frameOffsets[2] = {aFrame->GetPosition().x, aFrame->GetPosition().y};
for (uint8_t index = 0; index < 2; ++index) {
/* If the -moz-transform-origin specifies a percentage, take the percentage
* of the size of the box.
*/
const nsStyleCoord &coord = display->mTransformOrigin[index];
if (coord.GetUnit() == eStyleUnit_Calc) {
const nsStyleCoord::Calc *calc = coord.GetCalcValue();
if (coord.GetUnit() == eStyleUnit_Percent) {
coords[index] =
NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) *
calc->mPercent +
NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel);
} else if (coord.GetUnit() == eStyleUnit_Percent) {
coords[index] =
NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) *
coord.GetPercentValue();
NSAppUnitsToFloatPixels(boundingDimensions[index], aAppUnitsPerPixel) *
coord.GetPercentValue() +
NSAppUnitsToFloatPixels(boundingOffsets[index], aAppUnitsPerPixel);
} else {
NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
coords[index] =
NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel);
}
if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
coord.GetUnit() != eStyleUnit_Percent) {
// <length> values represent offsets from the origin of the SVG element's
// user space, not the top left of its bounds, so we must adjust for that:
nscoord offset =
(index == 0) ? aFrame->GetPosition().x : aFrame->GetPosition().y;
coords[index] -= NSAppUnitsToFloatPixels(offset, aAppUnitsPerPixel);
if (coord.GetUnit() == eStyleUnit_Calc) {
const nsStyleCoord::Calc *calc = coord.GetCalcValue();
coords[index] =
NSAppUnitsToFloatPixels(boundingDimensions[index], aAppUnitsPerPixel) *
calc->mPercent +
NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel);
} else {
NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
coords[index] =
NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel);
}
if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
// <length> values represent offsets from the origin of the SVG element's
// user space, not the top left of its border-box, so we must
// convert them to be relative to the border-box.
coords[index] -= NSAppUnitsToFloatPixels(frameOffsets[index], aAppUnitsPerPixel);
}
}
}
coords[2] = NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(),
aAppUnitsPerPixel);
/* Adjust based on the origin of the rectangle. */
coords[0] += NSAppUnitsToFloatPixels(boundingRect.x, aAppUnitsPerPixel);
coords[1] += NSAppUnitsToFloatPixels(boundingRect.y, aAppUnitsPerPixel);
return gfxPoint3D(coords[0], coords[1], coords[2]);
return gfxPoint3D(coords[0], coords[1],
NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(),
aAppUnitsPerPixel));
}
/* Returns the delta specified by the -moz-perspective-origin property.

View File

@ -124,3 +124,7 @@ skip-if(B2G) fails-if(Android) fuzzy-if(cocoaWidget,1,2) == stresstest-1.html st
== table-2b.html table-2-ref.html
# Bug 722463
== inline-1a.html inline-1-ref.html
== transform-origin-svg-1a.svg transform-origin-svg-1-ref.svg
== transform-origin-svg-1b.svg transform-origin-svg-1-ref.svg
== transform-origin-svg-2a.svg transform-origin-svg-2-ref.svg
== transform-origin-svg-2b.svg transform-origin-svg-2-ref.svg

View File

@ -0,0 +1,3 @@
<svg xmlns='http://www.w3.org/2000/svg'>
<rect x='40' y='140' width='100' height='100' fill='lime'/>
</svg>

After

Width:  |  Height:  |  Size: 108 B

View File

@ -0,0 +1,6 @@
<svg xmlns='http://www.w3.org/2000/svg'>
<g transform="translate(30,30)">
<rect x='10' y='10' width='100' height='100' fill='lime'
style="transform:rotate(90deg); transform-origin:left bottom;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 218 B

View File

@ -0,0 +1,7 @@
<svg xmlns='http://www.w3.org/2000/svg'>
<g transform="translate(30,30)">
<rect x='10' y='10' width='100' height='100' fill='lime'
style="transform:rotate(90deg); transform-origin:10px 110px;
-webkit-transform:rotate(90deg); -webkit-transform-origin:10px 110px;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 302 B

View File

@ -0,0 +1,3 @@
<svg xmlns='http://www.w3.org/2000/svg'>
<rect x='40' y='140' width='100' height='100' fill='lime' stroke-width='20' stroke='blue'/>
</svg>

After

Width:  |  Height:  |  Size: 140 B

View File

@ -0,0 +1,6 @@
<svg xmlns='http://www.w3.org/2000/svg'>
<g transform="translate(30,30)">
<rect x='10' y='10' width='100' height='100' fill='lime' stroke-width='20' stroke='blue'
style="transform:rotate(90deg); transform-origin:left bottom;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 250 B

View File

@ -0,0 +1,7 @@
<svg xmlns='http://www.w3.org/2000/svg'>
<g transform="translate(30,30)">
<rect x='10' y='10' width='100' height='100' fill='lime' stroke-width='20' stroke='blue'
style="transform:rotate(90deg); transform-origin:10px 110px;
-webkit-transform:rotate(90deg); -webkit-transform-origin:10px 110px;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 334 B

View File

@ -3687,14 +3687,16 @@ nsSVGTextFrame2::ReflowSVG()
nsSVGEffects::UpdateEffects(this);
}
// Now unset the various reflow bits. Do this before calling
// FinishAndStoreOverflow since FinishAndStoreOverflow can require glyph
// positions (to resolve transform-origin).
mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
NS_FRAME_HAS_DIRTY_CHILDREN);
nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
nsOverflowAreas overflowAreas(overflow, overflow);
FinishAndStoreOverflow(overflowAreas, mRect.Size());
// Now unset the various reflow bits:
mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
NS_FRAME_HAS_DIRTY_CHILDREN);
// XXX nsSVGContainerFrame::ReflowSVG only looks at its nsISVGChildFrame
// children, and calls ConsiderChildOverflow on them. Does it matter
// that ConsiderChildOverflow won't be called on our children?
@ -3729,9 +3731,22 @@ nsSVGTextFrame2::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
{
NS_ASSERTION(GetFirstPrincipalChild(), "must have a child frame");
SVGBBox bbox;
nsIFrame* kid = GetFirstPrincipalChild();
if (kid && NS_SUBTREE_DIRTY(kid)) {
// Return an empty bbox if our kid's subtree is dirty. This may be called
// in that situation, e.g. when we're building a display list after an
// interrupted reflow. This can also be called during reflow before we've
// been reflowed, e.g. if an earlier sibling is calling FinishAndStoreOverflow and
// needs our parent's perspective matrix, which depends on the SVG bbox
// contribution of this frame. In the latter situation, when all siblings have
// been reflowed, the parent will compute its perspective and rerun
// FinishAndStoreOverflow for all its children.
return bbox;
}
UpdateGlyphPositioning();
SVGBBox bbox;
nsPresContext* presContext = PresContext();
TextRenderedRunIterator it(this);

View File

@ -51,7 +51,7 @@ foreignObject {
text-indent: 0;
}
/* Set |transform-origin:0% 0%;| for all SVG elements except outer-<svg>,
/* Set |transform-origin:0 0;| for all SVG elements except outer-<svg>,
noting that 'svg' as a child of 'foreignObject' counts as outer-<svg>.
*/
*:not(svg),