Bug 745485. Optimize positioning offset changes whenever the computed size does not change. r=dholbert

--HG--
extra : rebase_source : 16388d3cb0c6ee9acc1416ca0d27b2a53b0062ee
This commit is contained in:
Robert O'Callahan 2013-09-09 17:08:41 -07:00
Родитель ca9efa851c
Коммит 9e62a53158
2 изменённых файлов: 69 добавлений и 50 удалений

Просмотреть файл

@ -386,59 +386,60 @@ RestyleManager::RecomputePosition(nsIFrame* aFrame)
return true;
}
// For absolute positioning, the width can potentially change if width is
// auto and either of left or right are not. The height can also potentially
// change if height is auto and either of top or bottom are not. In these
// cases we fall back to a reflow, and in all other cases, we attempt to
// move the frame here.
// Note that it is possible for the dimensions to not change in the above
// cases, so we should be a little smarter here and only fall back to reflow
// when the dimensions will really change (bug 745485).
const nsStylePosition* position = aFrame->StylePosition();
if (position->mWidth.GetUnit() != eStyleUnit_Auto &&
position->mHeight.GetUnit() != eStyleUnit_Auto) {
// For the absolute positioning case, set up a fake HTML reflow state for
// the frame, and then get the offsets from it.
nsRefPtr<nsRenderingContext> rc = aFrame->PresContext()->GetPresShell()->
GetReferenceRenderingContext();
// For the absolute positioning case, set up a fake HTML reflow state for
// the frame, and then get the offsets and size from it. If the frame's size
// doesn't need to change, we can simply update the frame position. Otherwise
// we fall back to a reflow.
nsRefPtr<nsRenderingContext> rc = aFrame->PresContext()->GetPresShell()->
GetReferenceRenderingContext();
// Construct a bogus parent reflow state so that there's a usable
// containing block reflow state.
nsIFrame* parentFrame = aFrame->GetParent();
nsSize parentSize = parentFrame->GetSize();
// Construct a bogus parent reflow state so that there's a usable
// containing block reflow state.
nsIFrame* parentFrame = aFrame->GetParent();
nsSize parentSize = parentFrame->GetSize();
nsFrameState savedState = parentFrame->GetStateBits();
nsHTMLReflowState parentReflowState(aFrame->PresContext(), parentFrame,
rc, parentSize);
parentFrame->RemoveStateBits(~nsFrameState(0));
parentFrame->AddStateBits(savedState);
nsFrameState savedState = parentFrame->GetStateBits();
nsHTMLReflowState parentReflowState(aFrame->PresContext(), parentFrame,
rc, parentSize);
parentFrame->RemoveStateBits(~nsFrameState(0));
parentFrame->AddStateBits(savedState);
NS_WARN_IF_FALSE(parentSize.width != NS_INTRINSICSIZE &&
parentSize.height != NS_INTRINSICSIZE,
"parentSize should be valid");
parentReflowState.SetComputedWidth(std::max(parentSize.width, 0));
parentReflowState.SetComputedHeight(std::max(parentSize.height, 0));
parentReflowState.mComputedMargin.SizeTo(0, 0, 0, 0);
parentSize.height = NS_AUTOHEIGHT;
NS_WARN_IF_FALSE(parentSize.width != NS_INTRINSICSIZE &&
parentSize.height != NS_INTRINSICSIZE,
"parentSize should be valid");
parentReflowState.SetComputedWidth(std::max(parentSize.width, 0));
parentReflowState.SetComputedHeight(std::max(parentSize.height, 0));
parentReflowState.mComputedMargin.SizeTo(0, 0, 0, 0);
parentReflowState.mComputedPadding = parentFrame->GetUsedPadding();
parentReflowState.mComputedBorderPadding =
parentFrame->GetUsedBorderAndPadding();
nsSize availSize(parentSize.width, NS_INTRINSICSIZE);
nsSize size = aFrame->GetSize();
ViewportFrame* viewport = do_QueryFrame(parentFrame);
nsSize cbSize = viewport ?
viewport->AdjustReflowStateAsContainingBlock(&parentReflowState).Size()
: aFrame->GetContainingBlock()->GetSize();
const nsMargin& parentBorder =
parentReflowState.mStyleBorder->GetComputedBorder();
cbSize -= nsSize(parentBorder.LeftRight(), parentBorder.TopBottom());
nsHTMLReflowState reflowState(aFrame->PresContext(), parentReflowState,
aFrame, availSize, cbSize.width,
cbSize.height);
parentReflowState.mComputedPadding = parentFrame->GetUsedPadding();
parentReflowState.mComputedBorderPadding =
parentFrame->GetUsedBorderAndPadding();
nsSize availSize(parentSize.width, NS_INTRINSICSIZE);
ViewportFrame* viewport = do_QueryFrame(parentFrame);
nsSize cbSize = viewport ?
viewport->AdjustReflowStateAsContainingBlock(&parentReflowState).Size()
: aFrame->GetContainingBlock()->GetSize();
const nsMargin& parentBorder =
parentReflowState.mStyleBorder->GetComputedBorder();
cbSize -= nsSize(parentBorder.LeftRight(), parentBorder.TopBottom());
nsHTMLReflowState reflowState(aFrame->PresContext(), parentReflowState,
aFrame, availSize, cbSize.width,
cbSize.height);
nsSize computedSize(reflowState.ComputedWidth(), reflowState.ComputedHeight());
computedSize.width += reflowState.mComputedBorderPadding.LeftRight();
if (computedSize.height != NS_INTRINSICSIZE) {
computedSize.height += reflowState.mComputedBorderPadding.TopBottom();
}
nsSize size = aFrame->GetSize();
// The RecomputePosition hint is not used if any offset changed between auto
// and non-auto. If computedSize.height == NS_INTRINSICSIZE then the new
// element height will be its intrinsic height, and since 'top' and 'bottom''s
// auto-ness hasn't changed, the old height must also be its intrinsic
// height, which we can assume hasn't changed (or reflow would have
// been triggered).
if (computedSize.width == size.width &&
(computedSize.height == NS_INTRINSICSIZE || computedSize.height == size.height)) {
// If we're solving for 'left' or 'top', then compute it here, in order to
// match the reflow code path.
if (NS_AUTOOFFSET == reflowState.mComputedOffsets.left) {

Просмотреть файл

@ -1278,6 +1278,18 @@ nsStylePosition::nsStylePosition(const nsStylePosition& aSource)
memcpy((nsStylePosition*)this, &aSource, sizeof(nsStylePosition));
}
static bool
IsAutonessEqual(const nsStyleSides& aSides1, const nsStyleSides& aSides2)
{
NS_FOR_CSS_SIDES(side) {
if ((aSides1.GetUnit(side) == eStyleUnit_Auto) !=
(aSides2.GetUnit(side) == eStyleUnit_Auto)) {
return false;
}
}
return true;
}
nsChangeHint nsStylePosition::CalcDifference(const nsStylePosition& aOther) const
{
nsChangeHint hint =
@ -1346,9 +1358,15 @@ nsChangeHint nsStylePosition::CalcDifference(const nsStylePosition& aOther) cons
// Note that it is possible that we'll need to reflow when processing
// restyles, but we don't have enough information to make a good decision
// right now.
// Don't try to handle changes between "auto" and non-auto efficiently;
// that's tricky to do and will hardly ever be able to avoid a reflow.
if (mOffset != aOther.mOffset) {
NS_UpdateHint(hint, nsChangeHint(nsChangeHint_RecomputePosition |
nsChangeHint_UpdateOverflow));
if (IsAutonessEqual(mOffset, aOther.mOffset)) {
NS_UpdateHint(hint, nsChangeHint(nsChangeHint_RecomputePosition |
nsChangeHint_UpdateOverflow));
} else {
return NS_CombineHint(hint, nsChangeHint_AllReflowHints);
}
}
return hint;
}