Bug 691651. When an element changes between having a transform and not having one, don't reframe if we don't need to. r=dbaron

--HG--
extra : rebase_source : ed990c17d2501ea5f4d09dea56baeada41cd3f35
This commit is contained in:
Robert O'Callahan 2012-08-10 23:16:49 +12:00
parent 65cf9f8b41
commit d2d218e577
9 changed files with 116 additions and 4 deletions

View File

@ -7976,6 +7976,31 @@ nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent,
NS_DECLARE_FRAME_PROPERTY(ChangeListProperty, nullptr)
/**
* Return true if aFrame's subtree has placeholders for abs-pos or fixed-pos
* content.
*/
static bool
FrameHasAbsPosPlaceholderDescendants(nsIFrame* aFrame)
{
const nsIFrame::ChildListIDs skip(nsIFrame::kAbsoluteList |
nsIFrame::kFixedList);
for (nsIFrame::ChildListIterator lists(aFrame); !lists.IsDone(); lists.Next()) {
if (!skip.Contains(lists.CurrentID())) {
for (nsFrameList::Enumerator childFrames(lists.CurrentList());
!childFrames.AtEnd(); childFrames.Next()) {
nsIFrame* f = childFrames.get();
if ((f->GetType() == nsGkAtoms::placeholderFrame &&
nsPlaceholderFrame::GetRealFrameForPlaceholder(f)->IsAbsolutelyPositioned()) ||
FrameHasAbsPosPlaceholderDescendants(f)) {
return true;
}
}
}
}
return false;
}
nsresult
nsCSSFrameConstructor::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
{
@ -8036,6 +8061,20 @@ nsCSSFrameConstructor::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
continue;
}
if ((hint & nsChangeHint_AddOrRemoveTransform) && frame &&
!(hint & nsChangeHint_ReconstructFrame)) {
if (FrameHasAbsPosPlaceholderDescendants(frame)) {
NS_UpdateHint(hint, nsChangeHint_ReconstructFrame);
} else {
// We can just add this state bit unconditionally, since it's
// conservative. Normally frame construction would set this if needed,
// but we're not going to reconstruct the frame so we need to set it.
// It's because we need to set this bit on each affected frame
// that we can't coalesce nsChangeHint_AddOrRemoveTransform hints up
// to ancestors (i.e. it can't be an inherited change hint).
frame->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
}
}
if (hint & nsChangeHint_ReconstructFrame) {
// If we ever start passing true here, be careful of restyles
// that involve a reframe and animations. In particular, if the

View File

@ -101,6 +101,14 @@ enum nsChangeHint {
*/
nsChangeHint_RecomputePosition = 0x2000,
/**
* Behaves like ReconstructFrame, but only if the frame has descendants
* that are absolutely or fixed position. Use this hint when a style change
* has changed whether the frame is a container for fixed-pos or abs-pos
* elements, but reframing is otherwise not needed.
*/
nsChangeHint_AddOrRemoveTransform = 0x4000,
/**
* We have an optimization when processing change hints which prevents
* us from visiting the descendants of a node when a hint on that node
@ -116,7 +124,8 @@ enum nsChangeHint {
nsChangeHint_UpdateOpacityLayer |
nsChangeHint_UpdateOverflow |
nsChangeHint_ChildrenOnlyTransform |
nsChangeHint_RecomputePosition
nsChangeHint_RecomputePosition |
nsChangeHint_AddOrRemoveTransform
};
// Redefine these operators to return nothing. This will catch any use

View File

@ -0,0 +1,8 @@
<!DOCTYPE HTML>
<html>
<body>
<div style="position:absolute; left:80px; top:20px; width:100px; height:100px; background:yellow">
<div style="position:absolute; left:50px; top:50px; width:100px; height:100px; background:orange"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<body style="margin:0;">
<div id="t" style="width:100px; height:100px; background:yellow;">
<div style="position:absolute; left:50px; top:50px; width:100px; height:100px; background:orange;"></div>
</div>
<script>
function doTest() {
document.getElementById("t").style.transform = "translate(80px,20px)";
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", doTest, false);
</script>
</body>
</html>

View File

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<body style="margin:0;">
<div id="t" style="width:100px; height:100px; background:yellow;">
<div style="position:fixed; left:50px; top:50px; width:100px; height:100px; background:orange;"></div>
</div>
<script>
function doTest() {
document.getElementById("t").style.transform = "translate(80px,20px)";
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", doTest, false);
</script>
</body>
</html>

View File

@ -0,0 +1,17 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<body style="margin:0;">
<div id="t" style="width:100px; height:100px; background:yellow;">
<div style="float:left;">
<div style="position:fixed; left:50px; top:50px; width:100px; height:100px; background:orange;"></div>
</div>
</div>
<script>
function doTest() {
document.getElementById("t").style.transform = "translate(80px,20px)";
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", doTest, false);
</script>
</body>
</html>

View File

@ -6,6 +6,9 @@
== compound-1a.html compound-1-ref.html
!= compound-1a.html compound-1-fail.html
== dynamic-inherit-1.html dynamic-inherit-1-ref.html
== dynamic-addremove-1a.html dynamic-addremove-1-ref.html
== dynamic-addremove-1b.html dynamic-addremove-1-ref.html
== dynamic-addremove-1c.html dynamic-addremove-1-ref.html
# translatex should act like position: relative
== translatex-1a.html translatex-1-ref.html
== translatex-1b.html translatex-1-ref.html

View File

@ -415,7 +415,7 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther)
// Visibility, Outline, TableBorder, Table, Text, UIReset, Quotes
nsChangeHint maxHint = nsChangeHint(NS_STYLE_HINT_FRAMECHANGE |
nsChangeHint_UpdateTransformLayer | nsChangeHint_UpdateOpacityLayer |
nsChangeHint_UpdateOverflow);
nsChangeHint_UpdateOverflow | nsChangeHint_AddOrRemoveTransform);
DO_STRUCT_DIFFERENCE(Display);
maxHint = nsChangeHint(NS_STYLE_HINT_FRAMECHANGE |

View File

@ -2276,7 +2276,12 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const
* or remove the view object, and also to handle abs-pos and fixed-pos containers.
*/
if (HasTransform() != aOther.HasTransform()) {
NS_UpdateHint(hint, nsChangeHint_ReconstructFrame);
// We do not need to apply nsChangeHint_UpdateTransformLayer since
// nsChangeHint_RepaintFrame will forcibly invalidate the frame area and
// ensure layers are rebuilt (or removed).
NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_AddOrRemoveTransform,
NS_CombineHint(nsChangeHint_UpdateOverflow,
nsChangeHint_RepaintFrame)));
}
else if (HasTransform()) {
/* Otherwise, if we've kept the property lying around and we already had a
@ -2339,7 +2344,8 @@ nsChangeHint nsStyleDisplay::MaxDifference()
return nsChangeHint(NS_STYLE_HINT_FRAMECHANGE |
nsChangeHint_UpdateOpacityLayer |
nsChangeHint_UpdateTransformLayer |
nsChangeHint_UpdateOverflow);
nsChangeHint_UpdateOverflow |
nsChangeHint_AddOrRemoveTransform);
}
#endif