mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-04 02:57:38 +00:00
Bug 876831 - Only reflow nsSVGTextFrame2's anonymous block frame under ReflowSVG. r=bzbarsky,longsonr f=jwatt
This commit is contained in:
parent
7135d9040a
commit
b8ecd886bc
@ -82,6 +82,7 @@ MOCHITEST_FILES = \
|
||||
test_text_2.html \
|
||||
test_text_scaled.html \
|
||||
test_text_selection.html \
|
||||
test_text_update.html \
|
||||
text-helper.svg \
|
||||
text-helper-scaled.svg \
|
||||
text-helper-selection.svg \
|
||||
|
31
content/svg/content/test/test_text_update.html
Normal file
31
content/svg/content/test/test_text_update.html
Normal file
@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<title>Test for Bug 876831</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=876831">Mozilla Bug 876831</a>
|
||||
<p id="display"</p>
|
||||
|
||||
<!--
|
||||
Test that the frame tree will be reflowed after a DOM mutation
|
||||
and just before an SVG DOM method does its work.
|
||||
-->
|
||||
|
||||
<svg>
|
||||
<text>ab</text>
|
||||
</svg>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="application/javascript">
|
||||
var text = document.querySelector("text");
|
||||
|
||||
var length = text.getComputedTextLength();
|
||||
ok(length > 0, "text.getComputedTextLength() > 0");
|
||||
|
||||
text.firstChild.nodeValue += "cd";
|
||||
ok(text.getComputedTextLength() > length, "text.getComputedTextLength() changes directly after DOM mutation");
|
||||
|
||||
text.removeChild(text.firstChild);
|
||||
is(text.getComputedTextLength(), 0, "text.getComputedTextLength() == 0 after removing child");
|
||||
</script>
|
||||
</pre>
|
18
layout/svg/crashtests/876831-1.svg
Normal file
18
layout/svg/crashtests/876831-1.svg
Normal file
@ -0,0 +1,18 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<script>
|
||||
|
||||
function boom()
|
||||
{
|
||||
var x = document.getElementById("x").firstChild;
|
||||
x.data = x.data.slice(1);
|
||||
document.caretPositionFromPoint(0, 0);
|
||||
}
|
||||
|
||||
window.addEventListener("load", boom, false);
|
||||
|
||||
</script>
|
||||
|
||||
<text><tspan id="x">@ت</tspan></text>
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 310 B |
@ -164,4 +164,5 @@ load 849688-2.svg
|
||||
load 860378-1.svg
|
||||
load 868904-1.svg
|
||||
load 873806-1.svg
|
||||
load 876831-1.svg
|
||||
load 877029-1.svg
|
||||
|
@ -2222,6 +2222,7 @@ private:
|
||||
mGlyphStartTextElementCharIndex = mTextElementCharIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The filter to use.
|
||||
*/
|
||||
@ -3067,7 +3068,7 @@ nsSVGTextFrame2::DidSetStyleContext(nsStyleContext* aOldStyleContext)
|
||||
// child to be reflowed when it is next painted, and (b) not cause the
|
||||
// <text> to be repainted anyway since the user of the <mask> would not
|
||||
// know it needs to be repainted.
|
||||
ReflowSVGNonDisplayText();
|
||||
ScheduleReflowSVGNonDisplayText();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3090,6 +3091,51 @@ nsSVGTextFrame2::ReflowSVGNonDisplayText()
|
||||
// be updated, which will then cause this nsSVGTextFrame2 to be painted and
|
||||
// in doing so cause the anonymous block frame to be reflowed.
|
||||
nsSVGEffects::InvalidateRenderingObservers(this);
|
||||
|
||||
// Finally, we need to actually reflow the anonymous block frame and update
|
||||
// mPositions, in case we are being reflowed immediately after a DOM
|
||||
// mutation that needs frame reconstruction.
|
||||
MaybeReflowAnonymousBlockChild();
|
||||
UpdateGlyphPositioning();
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGTextFrame2::ScheduleReflowSVGNonDisplayText()
|
||||
{
|
||||
MOZ_ASSERT(!nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
|
||||
"do not call ScheduleReflowSVGNonDisplayText when the outer SVG "
|
||||
"frame is under ReflowSVG");
|
||||
|
||||
nsSVGOuterSVGFrame* outerSVGFrame = nullptr;
|
||||
|
||||
nsIFrame* f = GetParent();
|
||||
while (f) {
|
||||
if (f->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
|
||||
outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(f);
|
||||
break;
|
||||
}
|
||||
if (!(f->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) {
|
||||
// We don't need to set NS_FRAME_HAS_DIRTY_CHILDREN on non-display
|
||||
// children in the path from the dirty nsSVGTextFrame2 up to the
|
||||
// first display container frame, since ReflowSVGNonDisplayText
|
||||
// does not check for it.
|
||||
if (NS_SUBTREE_DIRTY(f)) {
|
||||
return;
|
||||
}
|
||||
f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
|
||||
}
|
||||
f = f->GetParent();
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(outerSVGFrame &&
|
||||
outerSVGFrame->GetType() == nsGkAtoms::svgOuterSVGFrame,
|
||||
"Did not find nsSVGOuterSVGFrame!");
|
||||
NS_ABORT_IF_FALSE(!(outerSVGFrame->GetStateBits() & NS_FRAME_IN_REFLOW),
|
||||
"should not call ScheduleReflowSVGNonDisplayText under "
|
||||
"nsSVGOuterSVGFrame::Reflow");
|
||||
|
||||
PresContext()->PresShell()->FrameNeedsReflow(
|
||||
outerSVGFrame, nsIPresShell::eResize, NS_FRAME_HAS_DIRTY_CHILDREN);
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsSVGTextFrame2::MutationObserver, nsIMutationObserver)
|
||||
@ -3208,6 +3254,7 @@ nsSVGTextFrame2::FindCloserFrameForSelection(
|
||||
if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) {
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateGlyphPositioning();
|
||||
|
||||
nsPresContext* presContext = PresContext();
|
||||
@ -3515,7 +3562,7 @@ nsSVGTextFrame2::ReflowSVG()
|
||||
return;
|
||||
}
|
||||
|
||||
// UpdateGlyphPositioning will call DoReflow if necessary.
|
||||
MaybeReflowAnonymousBlockChild();
|
||||
UpdateGlyphPositioning();
|
||||
|
||||
nsPresContext* presContext = PresContext();
|
||||
@ -4819,6 +4866,20 @@ nsSVGTextFrame2::NotifyGlyphMetricsChange(uint32_t aFlags)
|
||||
|
||||
void
|
||||
nsSVGTextFrame2::UpdateGlyphPositioning()
|
||||
{
|
||||
nsIFrame* kid = GetFirstPrincipalChild();
|
||||
if (!kid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPositioningDirty) {
|
||||
MOZ_ASSERT(!NS_SUBTREE_DIRTY(kid), "should have already reflowed the kid");
|
||||
DoGlyphPositioning();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGTextFrame2::MaybeReflowAnonymousBlockChild()
|
||||
{
|
||||
nsIFrame* kid = GetFirstPrincipalChild();
|
||||
if (!kid)
|
||||
@ -4827,7 +4888,7 @@ nsSVGTextFrame2::UpdateGlyphPositioning()
|
||||
NS_ASSERTION(!(kid->GetStateBits() & NS_FRAME_IN_REFLOW),
|
||||
"should not be in reflow when about to reflow again");
|
||||
|
||||
if (mState & (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) {
|
||||
if (NS_SUBTREE_DIRTY(this)) {
|
||||
if (mState & NS_FRAME_IS_DIRTY) {
|
||||
// If we require a full reflow, ensure our kid is marked fully dirty.
|
||||
// (Note that our anonymous nsBlockFrame is not an nsISVGChildFrame, so
|
||||
@ -4840,10 +4901,6 @@ nsSVGTextFrame2::UpdateGlyphPositioning()
|
||||
nsPresContext::InterruptPreventer noInterrupts(PresContext());
|
||||
DoReflow();
|
||||
}
|
||||
|
||||
if (mPositioningDirty) {
|
||||
DoGlyphPositioning();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -294,14 +294,33 @@ public:
|
||||
enum { ePositioningDirtyDueToMutation = 1 };
|
||||
|
||||
/**
|
||||
* Performs any operations required when this frame is non-display and
|
||||
* has had a style change. See the two callers for more information:
|
||||
* ReflowSVGNonDisplayText in nsSVGContainer.cpp (called under
|
||||
* nsSVGDisplayContainerFrame::ReflowSVG) and
|
||||
* nsSVGTextFrame2::DidSetStyleContext.
|
||||
* Reflows the anonymous block frame of this non-display nsSVGTextFrame2.
|
||||
*
|
||||
* When we are under nsSVGDisplayContainerFrame::ReflowSVG, we need to
|
||||
* reflow any nsSVGTextFrame2 frames in the subtree in case they are
|
||||
* being observed (by being for example in a <mask>) and the change
|
||||
* that caused the reflow would not already have caused a reflow.
|
||||
*
|
||||
* Note that displayed nsSVGTextFrame2s are reflowed as needed, when PaintSVG
|
||||
* is called or some SVG DOM method is called on the element.
|
||||
*/
|
||||
void ReflowSVGNonDisplayText();
|
||||
|
||||
/**
|
||||
* This is a function that behaves similarly to nsSVGUtils::ScheduleReflowSVG,
|
||||
* but which will skip over any ancestor non-display container frames on the
|
||||
* way to the nsSVGOuterSVGFrame. It exists for the situation where a
|
||||
* non-display <text> element has changed and needs to ensure ReflowSVG will
|
||||
* be called on its closest display container frame, so that
|
||||
* nsSVGDisplayContainerFrame::ReflowSVG will call ReflowSVGNonDisplayText on
|
||||
* it.
|
||||
*
|
||||
* The only case where we have to do this is in response to a style change on
|
||||
* a non-display <text>; the only caller of ScheduleReflowSVGNonDisplayText
|
||||
* currently is nsSVGTextFrame2::DidSetStyleContext.
|
||||
*/
|
||||
void ScheduleReflowSVGNonDisplayText();
|
||||
|
||||
/**
|
||||
* Updates the mFontSizeScaleFactor value by looking at the range of
|
||||
* font-sizes used within the <text>.
|
||||
@ -352,7 +371,6 @@ private:
|
||||
}
|
||||
~AutoCanvasTMForMarker()
|
||||
{
|
||||
// Default
|
||||
mFrame->mGetCanvasTMForFlag = mOldFor;
|
||||
}
|
||||
private:
|
||||
@ -399,17 +417,19 @@ private:
|
||||
};
|
||||
|
||||
/**
|
||||
* Reflows the anonymous block child.
|
||||
* Reflows the anonymous block child if it is dirty or has dirty
|
||||
* children, or if the nsSVGTextFrame2 itself is dirty.
|
||||
*/
|
||||
void MaybeReflowAnonymousBlockChild();
|
||||
|
||||
/**
|
||||
* Performs the actual work of reflowing the anonymous block child.
|
||||
*/
|
||||
void DoReflow();
|
||||
|
||||
/**
|
||||
* Calls FrameNeedsReflow on the anonymous block child.
|
||||
*/
|
||||
void RequestReflow(nsIPresShell::IntrinsicDirty aType, uint32_t aBit);
|
||||
|
||||
/**
|
||||
* Reflows the anonymous block child and recomputes mPositions if needed.
|
||||
* Recomputes mPositions by calling DoGlyphPositioning if this information
|
||||
* is out of date.
|
||||
*/
|
||||
void UpdateGlyphPositioning();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user