Bug 1632765 - Bypass a restyling assertion in a replicated fixed-pos subtree. r=TYLin

The pref flip in this bug causes an assertion to fail in
layout/generic/crashtests/1137723-1.html.

Our behavior in that crashtest is so messed up that I can't even begin
to describe it.

That test-case has three-pages, and a link inside a fixed-pos subtree,
which has a ::after pseudo-element.

Via the magic of nsCSSFrameConstructor::ReplicateFixedFrames, we end up
constructing multiple frames, one per page, for the fixed subtree.

We end up with a link with three different ::after pseudo-elements (one
on each page), of which the link only knows about the latest one.

This means that when restyling the link (which was already broken, it
just didn't happen before the prefs), we'd visit the pseudo-element in
some other place of the frame tree we can get a hand on.

Restyling these frames is generally not supported and will do ~nothing,
given the current setup. There's no way to get a hand from the DOM node
to all its replicated frames.

But that's not something I plan to fix for this bug, and this assertion
is blocking me.

Differential Revision: https://phabricator.services.mozilla.com/D72755
This commit is contained in:
Emilio Cobos Álvarez 2020-04-28 09:24:25 +00:00
parent 64c748af66
commit f7004f36cc

View File

@ -1989,6 +1989,25 @@ static const nsIFrame* ExpectedOwnerForChild(const nsIFrame* aFrame) {
return parent;
}
// FIXME(emilio, bug 1633685): We should ideally figure out how to properly
// restyle replicated fixed pos frames... We seem to assume everywhere that they
// can't get restyled at the moment...
static bool IsInReplicatedFixedPosTree(const nsIFrame* aFrame) {
if (!aFrame->PresContext()->IsPaginated()) {
return false;
}
for (; aFrame; aFrame = aFrame->GetParent()) {
if (aFrame->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
!aFrame->FirstContinuation()->IsPrimaryFrame() &&
nsLayoutUtils::IsReallyFixedPos(aFrame)) {
return true;
}
}
return true;
}
void ServoRestyleState::AssertOwner(const ServoRestyleState& aParent) const {
MOZ_ASSERT(mOwner);
MOZ_ASSERT(!mOwner->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));
@ -1999,7 +2018,7 @@ void ServoRestyleState::AssertOwner(const ServoRestyleState& aParent) const {
// chains of ServoRestyleStates in some cases where it's just not worth it.
if (aParent.mOwner) {
const nsIFrame* owner = ExpectedOwnerForChild(mOwner);
if (owner != aParent.mOwner) {
if (owner != aParent.mOwner && !IsInReplicatedFixedPosTree(mOwner)) {
MOZ_ASSERT(IsAnonBox(owner),
"Should only have expected owner weirdness when anon boxes "
"are involved");
@ -2022,7 +2041,8 @@ nsChangeHint ServoRestyleState::ChangesHandledFor(
return mChangesHandled;
}
MOZ_ASSERT(mOwner == ExpectedOwnerForChild(aFrame),
MOZ_ASSERT(mOwner == ExpectedOwnerForChild(aFrame) ||
IsInReplicatedFixedPosTree(aFrame),
"Missed some frame in the hierarchy?");
return mChangesHandled;
}