Bug 898909 - Reflow non-display SVG text under non-display outer <svg> frames. r=jwatt

This commit is contained in:
Cameron McCormack 2013-07-30 09:47:30 +10:00
parent eb70871278
commit 707aa0fde3
5 changed files with 74 additions and 55 deletions

View File

@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" requiredFeatures="foo">
<text id="t" />
<script>
window.addEventListener("load", function() {
document.getElementById("t").getComputedTextLength();
}, false);
</script>
</svg>

After

Width:  |  Height:  |  Size: 239 B

View File

@ -174,4 +174,5 @@ load 890783-1.svg
load 893510-1.svg
load 895311-1.svg
load 897342-1.svg
load 898909-1.svg
load 898951-1.svg

View File

@ -82,6 +82,53 @@ nsSVGContainerFrame::UpdateOverflow()
return nsSVGContainerFrameBase::UpdateOverflow();
}
/**
* Traverses a frame tree, marking any nsSVGTextFrame2 frames as dirty
* and calling InvalidateRenderingObservers() on it.
*
* The reason that this helper exists is because nsSVGTextFrame2 is special.
* None of the other SVG frames ever need to be reflowed when they have the
* NS_FRAME_IS_NONDISPLAY bit set on them because their PaintSVG methods
* (and those of any containers that they can validly be contained within) do
* not make use of mRect or overflow rects. "em" lengths, etc., are resolved
* as those elements are painted.
*
* nsSVGTextFrame2 is different because its anonymous block and inline frames
* need to be reflowed in order to get the correct metrics when things like
* inherited font-size of an ancestor changes, or a delayed webfont loads and
* applies.
*
* We assume that any change that requires the anonymous kid of an
* nsSVGTextFrame2 to reflow will result in an NS_FRAME_IS_DIRTY reflow. When
* that reflow reaches an NS_FRAME_IS_NONDISPLAY frame it would normally
* stop, but this helper looks for any nsSVGTextFrame2 descendants of such
* frames and marks them NS_FRAME_IS_DIRTY so that the next time that they are
* painted their anonymous kid will first get the necessary reflow.
*/
/* static */ void
nsSVGContainerFrame::ReflowSVGNonDisplayText(nsIFrame* aContainer)
{
NS_ASSERTION(aContainer->GetStateBits() & NS_FRAME_IS_DIRTY,
"expected aContainer to be NS_FRAME_IS_DIRTY");
NS_ASSERTION((aContainer->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ||
!aContainer->IsFrameOfType(nsIFrame::eSVG),
"it is wasteful to call ReflowSVGNonDisplayText on a container "
"frame that is not NS_FRAME_IS_NONDISPLAY");
for (nsIFrame* kid = aContainer->GetFirstPrincipalChild(); kid;
kid = kid->GetNextSibling()) {
nsIAtom* type = kid->GetType();
if (type == nsGkAtoms::svgTextFrame2) {
static_cast<nsSVGTextFrame2*>(kid)->ReflowSVGNonDisplayText();
} else {
if (kid->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer) ||
type == nsGkAtoms::svgForeignObjectFrame ||
!kid->IsFrameOfType(nsIFrame::eSVG)) {
ReflowSVGNonDisplayText(kid);
}
}
}
}
void
nsSVGDisplayContainerFrame::Init(nsIContent* aContent,
nsIFrame* aParent,
@ -242,53 +289,6 @@ nsSVGDisplayContainerFrame::GetCoveredRegion()
return nsSVGUtils::GetCoveredRegion(mFrames);
}
/**
* Traverses a frame tree, marking any nsSVGTextFrame2 frames as dirty
* and calling InvalidateRenderingObservers() on it.
*
* The reason that this helper exists is because nsSVGTextFrame2 is special.
* None of the other SVG frames ever need to be reflowed when they have the
* NS_FRAME_IS_NONDISPLAY bit set on them because their PaintSVG methods
* (and those of any containers that they can validly be contained within) do
* not make use of mRect or overflow rects. "em" lengths, etc., are resolved
* as those elements are painted.
*
* nsSVGTextFrame2 is different because its anonymous block and inline frames
* need to be reflowed in order to get the correct metrics when things like
* inherited font-size of an ancestor changes, or a delayed webfont loads and
* applies.
*
* We assume that any change that requires the anonymous kid of an
* nsSVGTextFrame2 to reflow will result in an NS_FRAME_IS_DIRTY reflow. When
* that reflow reaches an NS_FRAME_IS_NONDISPLAY frame it would normally
* stop, but this helper looks for any nsSVGTextFrame2 descendants of such
* frames and marks them NS_FRAME_IS_DIRTY so that the next time that they are
* painted their anonymous kid will first get the necessary reflow.
*/
static void
ReflowSVGNonDisplayText(nsIFrame* aContainer)
{
NS_ASSERTION(aContainer->GetStateBits() & NS_FRAME_IS_DIRTY,
"expected aContainer to be NS_FRAME_IS_DIRTY");
NS_ASSERTION((aContainer->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ||
!aContainer->IsFrameOfType(nsIFrame::eSVG),
"it is wasteful to call ReflowSVGNonDisplayText on a container "
"frame that is not NS_FRAME_IS_NONDISPLAY");
for (nsIFrame* kid = aContainer->GetFirstPrincipalChild(); kid;
kid = kid->GetNextSibling()) {
nsIAtom* type = kid->GetType();
if (type == nsGkAtoms::svgTextFrame2) {
static_cast<nsSVGTextFrame2*>(kid)->ReflowSVGNonDisplayText();
} else {
if (kid->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer) ||
type == nsGkAtoms::svgForeignObjectFrame ||
!kid->IsFrameOfType(nsIFrame::eSVG)) {
ReflowSVGNonDisplayText(kid);
}
}
}
}
void
nsSVGDisplayContainerFrame::ReflowSVG()
{

View File

@ -91,6 +91,13 @@ public:
const nsDisplayListSet& aLists) MOZ_OVERRIDE {}
virtual bool UpdateOverflow() MOZ_OVERRIDE;
protected:
/**
* Traverses a frame tree, marking any nsSVGTextFrame2 frames as dirty
* and calling InvalidateRenderingObservers() on it.
*/
static void ReflowSVGNonDisplayText(nsIFrame* aContainer);
};
/**

View File

@ -463,21 +463,21 @@ nsSVGOuterSVGFrame::Reflow(nsPresContext* aPresContext,
}
mViewportInitialized = true;
if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
// Now that we've marked the necessary children as dirty, call
// ReflowSVG() on them:
mCallingReflowSVG = true;
// Now that we've marked the necessary children as dirty, call
// ReflowSVG() or ReflowSVGNonDisplayText() on them, depending
// on whether we are non-display.
mCallingReflowSVG = true;
if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
ReflowSVGNonDisplayText(this);
} else {
// Update the mRects and visual overflow rects of all our descendants,
// including our anonymous wrapper kid:
anonKid->AddStateBits(mState & NS_FRAME_IS_DIRTY);
anonKid->ReflowSVG();
NS_ABORT_IF_FALSE(!anonKid->GetNextSibling(),
"We should have one anonymous child frame wrapping our real children");
mCallingReflowSVG = false;
}
mCallingReflowSVG = false;
// Set our anonymous kid's offset from our border box:
anonKid->SetPosition(GetContentRectRelativeToSelf().TopLeft());