Bug 827577. Be a lot more careful about saving and restoring frame constructor state for fixed-pos descendants of CSS-transformed elements. r=bzbarsky

--HG--
extra : rebase_source : b78a8cb774b961696e59c2c27a8ade56d629b78f
This commit is contained in:
Robert O'Callahan 2013-01-11 00:47:33 +13:00
parent 69fa838f1a
commit 7ab390b69a
7 changed files with 108 additions and 17 deletions

View File

@ -683,16 +683,19 @@ public:
~nsFrameConstructorSaveState();
private:
nsAbsoluteItems* mItems; // pointer to struct whose data we save/restore
bool* mFixedPosIsAbsPos;
nsAbsoluteItems mSavedItems; // copy of original data
bool mSavedFixedPosIsAbsPos;
nsAbsoluteItems* mItems; // pointer to struct whose data we save/restore
nsAbsoluteItems mSavedItems; // copy of original data
// The name of the child list in which our frames would belong
ChildListID mChildListID;
nsFrameConstructorState* mState;
// State used only when we're saving the abs-pos state for a transformed
// element.
nsAbsoluteItems mSavedFixedItems;
bool mSavedFixedPosIsAbsPos;
friend class nsFrameConstructorState;
};
@ -781,6 +784,8 @@ public:
// Function to push the existing absolute containing block state and
// create a new scope. Code that uses this function should get matching
// logic in GetAbsoluteContainingBlock.
// Also makes aNewAbsoluteContainingBlock the containing block for
// fixed-pos elements if necessary.
void PushAbsoluteContainingBlock(nsIFrame* aNewAbsoluteContainingBlock,
nsFrameConstructorSaveState& aSaveState);
@ -1027,11 +1032,15 @@ nsFrameConstructorState::PushAbsoluteContainingBlock(nsIFrame* aNewAbsoluteConta
aSaveState.mSavedItems = mAbsoluteItems;
aSaveState.mChildListID = nsIFrame::kAbsoluteList;
aSaveState.mState = this;
/* Store whether we're wiring the abs-pos and fixed-pos lists together. */
aSaveState.mFixedPosIsAbsPos = &mFixedPosIsAbsPos;
aSaveState.mSavedFixedPosIsAbsPos = mFixedPosIsAbsPos;
if (mFixedPosIsAbsPos) {
// Since we're going to replace mAbsoluteItems, we need to save it into
// mFixedItems now (and save the current value of mFixedItems).
aSaveState.mSavedFixedItems = mFixedItems;
mFixedItems = mAbsoluteItems;
}
mAbsoluteItems =
nsAbsoluteItems(AdjustAbsoluteContainingBlock(aNewAbsoluteContainingBlock));
@ -1235,7 +1244,13 @@ nsFrameConstructorState::ProcessFrameInsertions(nsAbsoluteItems& aFrameItems,
NS_ASSERTION(containingBlock,
"Child list without containing block?");
if (aChildListID == nsIFrame::kFixedList &&
containingBlock->GetStyleDisplay()->HasTransform(containingBlock)) {
// Put this frame on the transformed-frame's abs-pos list instead.
aChildListID = nsIFrame::kAbsoluteList;
}
// Insert the frames hanging out in aItems. We can use SetInitialChildList()
// if the containing block hasn't been reflowed yet (so NS_FRAME_FIRST_REFLOW
// is set) and doesn't have any frames in the aChildListID child list yet.
@ -1299,11 +1314,11 @@ nsFrameConstructorState::ProcessFrameInsertions(nsAbsoluteItems& aFrameItems,
nsFrameConstructorSaveState::nsFrameConstructorSaveState()
: mItems(nullptr),
mFixedPosIsAbsPos(nullptr),
mSavedItems(nullptr),
mSavedFixedPosIsAbsPos(false),
mChildListID(kPrincipalList),
mState(nullptr)
mState(nullptr),
mSavedFixedItems(nullptr),
mSavedFixedPosIsAbsPos(false)
{
}
@ -1319,9 +1334,20 @@ nsFrameConstructorSaveState::~nsFrameConstructorSaveState()
// Note that this only matters for the assert in ~nsAbsoluteItems.
mSavedItems.Clear();
#endif
}
if (mFixedPosIsAbsPos) {
*mFixedPosIsAbsPos = mSavedFixedPosIsAbsPos;
if (mItems == &mState->mAbsoluteItems) {
mState->mFixedPosIsAbsPos = mSavedFixedPosIsAbsPos;
if (mSavedFixedPosIsAbsPos) {
// mAbsoluteItems was moved to mFixedItems, so move mFixedItems back
// and repair the old mFixedItems now.
mState->mAbsoluteItems = mState->mFixedItems;
mState->mFixedItems = mSavedFixedItems;
#ifdef DEBUG
mSavedFixedItems.Clear();
#endif
}
}
NS_ASSERTION(!mItems->LastChild() || !mItems->LastChild()->GetNextSibling(),
"Something corrupted our list");
}
}
@ -5613,6 +5639,21 @@ nsCSSFrameConstructor::GetAbsoluteContainingBlock(nsIFrame* aFrame)
return mHasRootAbsPosContainingBlock ? mDocElementContainingBlock : nullptr;
}
nsIFrame*
nsCSSFrameConstructor::GetFixedContainingBlock(nsIFrame* aFrame)
{
NS_PRECONDITION(nullptr != mRootElementFrame, "no root element frame");
// Starting with aFrame, look for a frame that is CSS-transformed
for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
if (frame->GetStyleDisplay()->HasTransform(frame)) {
return frame;
}
}
return mFixedContainingBlock;
}
nsIFrame*
nsCSSFrameConstructor::GetFloatContainingBlock(nsIFrame* aFrame)
{
@ -6607,7 +6648,7 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
&parentAfterFrame);
// Create some new frames
nsFrameConstructorState state(mPresShell, mFixedContainingBlock,
nsFrameConstructorState state(mPresShell, GetFixedContainingBlock(parentFrame),
GetAbsoluteContainingBlock(parentFrame),
GetFloatContainingBlock(parentFrame));
state.mTreeMatchContext.InitAncestors(aContainer->AsElement());
@ -7042,7 +7083,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
return rv;
}
nsFrameConstructorState state(mPresShell, mFixedContainingBlock,
nsFrameConstructorState state(mPresShell, GetFixedContainingBlock(parentFrame),
GetAbsoluteContainingBlock(parentFrame),
GetFloatContainingBlock(parentFrame),
aFrameState);

View File

@ -1464,6 +1464,7 @@ private:
*/
public:
nsIFrame* GetAbsoluteContainingBlock(nsIFrame* aFrame);
nsIFrame* GetFixedContainingBlock(nsIFrame* aFrame);
private:
nsIFrame* GetFloatContainingBlock(nsIFrame* aFrame);

View File

@ -0,0 +1,7 @@
<!DOCTYPE HTML>
<html>
<body>
<div style="margin-left:100px; width:200px; height:200px; border:1px solid black">
<div style="margin-left:150px; width:50px; height:80px; background:yellow;">
</div>
</div>

View File

@ -0,0 +1,11 @@
<!DOCTYPE HTML>
<html>
<body>
<div style="transform:translateX(100px); width:200px; height:200px; border:1px solid black">
<div style="position:absolute;">
</div>
<div style="position:absolute;">
<div style="position:fixed; right:0; width:50px; height:80px; background:yellow;">
</div>
</div>
</div>

View File

@ -0,0 +1,18 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<body>
<div style="transform:translateX(100px); width:200px; height:200px; border:1px solid black">
<div style="position:absolute;">
</div>
<div style="position:absolute;">
<div id="d" style="display:none; position:fixed; right:0; width:50px; height:80px; background:yellow;">
</div>
</div>
</div>
<script>
function doTest() {
document.getElementById("d").style.display = "";
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", doTest, false);
</script>

View File

@ -0,0 +1,11 @@
<!DOCTYPE HTML>
<html class="reftest-wait>
<body>
<div style="transform:translateX(100px); width:200px; height:200px; border:1px solid black">
<div style="position:absolute;">
</div>
<div style="position:absolute;">
<div style="position:fixed; right:0; width:50px; height:80px; background:yellow;">
</div>
</div>
</div>

View File

@ -1738,3 +1738,5 @@ skip-if(B2G) == 814952-1.html 814952-1-ref.html
== 816458-1.html 816458-1-ref.html
== 816359-1.html 816359-1-ref.html
skip-if(B2G) == 818276-1.html 818276-1-ref.html
== 827577-1a.html 827577-1-ref.html
== 827577-1b.html 827577-1-ref.html