Bug 1388625 part 5. Implement wrapper anonymous box restyling in ServoRestyleManager. r?heycam
MozReview-Commit-ID: FRW4RCR1GT4
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -45,40 +45,44 @@ FirstContinuationOrPartOfIBSplit(const n
}
return nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
}
static const nsIFrame*
ExpectedOwnerForChild(const nsIFrame& aFrame)
{
+ const nsIFrame* parent = aFrame.GetParent();
+ if (aFrame.IsTableFrame()) {
+ MOZ_ASSERT(parent->IsTableWrapperFrame());
+ parent = parent->GetParent();
+ }
+
if (IsAnonBox(aFrame) && !aFrame.IsTextFrame()) {
- return aFrame.GetParent()->IsViewportFrame() ? nullptr : aFrame.GetParent();
+ if (parent->IsLineFrame()) {
+ parent = parent->GetParent();
+ }
+ return parent->IsViewportFrame() ? nullptr : parent;
}
if (aFrame.IsBulletFrame()) {
- return aFrame.GetParent();
+ return parent;
}
if (aFrame.IsLineFrame()) {
// A ::first-line always ends up here via its block, which is therefore the
// right expected owner. That block can be an
// anonymous box. For example, we could have a ::first-line on a columnated
// block; the blockframe is the column-content anonymous box in that case.
// So we don't want to end up in the code below, which steps out of anon
// boxes. Just return the parent of the line frame, which is the block.
- return aFrame.GetParent();
+ return parent;
}
- const nsIFrame* parent = FirstContinuationOrPartOfIBSplit(aFrame.GetParent());
-
- if (aFrame.IsTableFrame()) {
- MOZ_ASSERT(parent->IsTableWrapperFrame());
- parent = FirstContinuationOrPartOfIBSplit(parent->GetParent());
- }
+ parent = FirstContinuationOrPartOfIBSplit(parent);
// We've handled already anon boxes and bullet frames, so now we're looking at
// a frame of a DOM element or pseudo. Hop through anon and line-boxes
// generated by our DOM parent, and go find the owner frame for it.
while (parent && (IsAnonBox(*parent) || parent->IsLineFrame())) {
auto* pseudo = parent->StyleContext()->GetPseudo();
if (pseudo == nsCSSAnonBoxes::tableWrapper) {
const nsIFrame* tableFrame = parent->PrincipalChildList().FirstChild();
@@ -114,16 +118,164 @@ ServoRestyleState::ChangesHandledFor(con
}
MOZ_ASSERT(mOwner == ExpectedOwnerForChild(aFrame),
"Missed some frame in the hierarchy?");
return mChangesHandled;
}
#endif
+void
+ServoRestyleState::AddPendingWrapperRestyle(nsIFrame* aWrapperFrame)
+{
+ MOZ_ASSERT(aWrapperFrame->StyleContext()->IsWrapperAnonBox(),
+ "All our wrappers are anon boxes, and why would we restyle "
+ "non-inheriting ones?");
+ MOZ_ASSERT(aWrapperFrame->StyleContext()->IsInheritingAnonBox(),
+ "All our wrappers are anon boxes, and why would we restyle "
+ "non-inheriting ones?");
+ MOZ_ASSERT(aWrapperFrame->StyleContext()->GetPseudo() !=
+ nsCSSAnonBoxes::cellContent,
+ "Someone should be using TableAwareParentFor");
+ MOZ_ASSERT(aWrapperFrame->StyleContext()->GetPseudo() !=
+ nsCSSAnonBoxes::tableWrapper,
+ "Someone should be using TableAwareParentFor");
+ // Make sure we only add first continuations.
+ aWrapperFrame = aWrapperFrame->FirstContinuation();
+ nsIFrame* last = mPendingWrapperRestyles.SafeLastElement(nullptr);
+ if (last == aWrapperFrame) {
+ // Already queued up, nothing to do.
+ return;
+ }
+
+ // Make sure to queue up parents before children. But don't queue up
+ // ancestors of non-anonymous boxes here; those are handled when we traverse
+ // their non-anonymous kids.
+ if (aWrapperFrame->ParentIsWrapperAnonBox()) {
+ AddPendingWrapperRestyle(TableAwareParentFor(aWrapperFrame));
+ }
+
+ // If the append fails, we'll fail to restyle properly, but that's probably
+ // better than crashing.
+ if (mPendingWrapperRestyles.AppendElement(aWrapperFrame, fallible)) {
+ aWrapperFrame->SetIsWrapperAnonBoxNeedingRestyle(true);
+ }
+}
+
+void
+ServoRestyleState::ProcessWrapperRestyles(nsIFrame* aParentFrame)
+{
+ size_t i = mPendingWrapperRestyleOffset;
+ while (i < mPendingWrapperRestyles.Length()) {
+ i += ProcessMaybeNestedWrapperRestyle(aParentFrame, i);
+ }
+
+ mPendingWrapperRestyles.TruncateLength(mPendingWrapperRestyleOffset);
+}
+
+size_t
+ServoRestyleState::ProcessMaybeNestedWrapperRestyle(nsIFrame* aParent,
+ size_t aIndex)
+{
+ // The frame at index aIndex is something we should restyle ourselves, but
+ // following frames may need separate ServoRestyleStates to restyle.
+ MOZ_ASSERT(aIndex < mPendingWrapperRestyles.Length());
+
+ nsIFrame* cur = mPendingWrapperRestyles[aIndex];
+ MOZ_ASSERT(cur->StyleContext()->IsWrapperAnonBox());
+
+ // Where is cur supposed to inherit from? From its parent frame, except in
+ // the case when cur is a table, in which case it should be its grandparent.
+ // Also, not in the case when the resulting frame would be a first-line; in
+ // that case we should be inheriting from the block, and the first-line will
+ // do its fixup later if needed.
+ //
+ // Note that after we do all that fixup the parent we get might still not be
+ // aParent; for example aParent could be a scrollframe, in which case we
+ // should inherit from the scrollcontent frame. Or the parent might be some
+ // continuation of aParent.
+ //
+ // Try to assert as much as we can about the parent we actually end up using
+ // without triggering bogus asserts in all those various edge cases.
+ nsIFrame* parent = cur->GetParent();
+ if (cur->IsTableFrame()) {
+ MOZ_ASSERT(parent->IsTableWrapperFrame());
+ parent = parent->GetParent();
+ }
+ if (parent->IsLineFrame()) {
+ parent = parent->GetParent();
+ }
+ MOZ_ASSERT(parent->FirstContinuation() == aParent ||
+ (parent->StyleContext()->IsInheritingAnonBox() &&
+ parent->GetContent() == aParent->GetContent()));
+
+ // Now "this" is a ServoRestyleState for aParent, so if parent != aParent we
+ // need a new ServoRestyleState for the kid.
+ Maybe<ServoRestyleState> parentRestyleState;
+ if (parent != aParent) {
+ parentRestyleState.emplace(*parent, *this, nsChangeHint_Empty,
+ Type::InFlow);
+ }
+ ServoRestyleState& curRestyleState =
+ parentRestyleState ? *parentRestyleState : *this;
+
+ // This frame may already have been restyled. Even if it has, we can't just
+ // return, because the next frame may be a kid of it that does need restyling.
+ if (cur->IsWrapperAnonBoxNeedingRestyle()) {
+ parent->UpdateStyleOfChildAnonBox(cur, curRestyleState);
+ cur->SetIsWrapperAnonBoxNeedingRestyle(false);
+ }
+
+ size_t numProcessed = 1;
+
+ // Note: no overflow possible here, since aIndex < length.
+ if (aIndex + 1 < mPendingWrapperRestyles.Length()) {
+ nsIFrame* next = mPendingWrapperRestyles[aIndex + 1];
+ if (TableAwareParentFor(next) == cur &&
+ next->IsWrapperAnonBoxNeedingRestyle()) {
+ // It might be nice if we could do better than nsChangeHint_Empty. On
+ // the other hand, presumably our mChangesHandled already has the bits
+ // we really want here so in practice it doesn't matter.
+ ServoRestyleState childState(*cur, curRestyleState, nsChangeHint_Empty,
+ Type::InFlow,
+ /* aAssertWrapperRestyleLength = */ false);
+ numProcessed += childState.ProcessMaybeNestedWrapperRestyle(cur,
+ aIndex + 1);
+ }
+ }
+
+ return numProcessed;
+}
+
+nsIFrame*
+ServoRestyleState::TableAwareParentFor(const nsIFrame* aChild)
+{
+ // We want to get the anon box parent for aChild. where aChild has
+ // ParentIsWrapperAnonBox().
+ //
+ // For the most part this is pretty straightforward, but there are two
+ // wrinkles. First, if aChild is a table, then we really want the parent of
+ // its table wrapper.
+ if (aChild->IsTableFrame()) {
+ aChild = aChild->GetParent();
+ MOZ_ASSERT(aChild->IsTableWrapperFrame());
+ }
+
+ nsIFrame* parent = aChild->GetParent();
+ // Now if parent is a cell-content frame, we actually want the cellframe.
+ if (parent->StyleContext()->GetPseudo() == nsCSSAnonBoxes::cellContent) {
+ parent = parent->GetParent();
+ } else if (parent->IsTableWrapperFrame()) {
+ // Must be a caption. In that case we want the table here.
+ MOZ_ASSERT(aChild->StyleDisplay()->mDisplay == StyleDisplay::TableCaption);
+ parent = parent->PrincipalChildList().FirstChild();
+ }
+ return parent;
+}
+
ServoRestyleManager::ServoRestyleManager(nsPresContext* aPresContext)
: RestyleManager(StyleBackendType::Servo, aPresContext)
, mReentrantChanges(nullptr)
{
}
void
ServoRestyleManager::PostRestyleEvent(Element* aElement,
@@ -430,17 +582,18 @@ UpdateBackdropIfNeeded(nsIFrame* aFrame,
aStyleSet.ResolvePseudoElementStyle(aFrame->GetContent()->AsElement(),
CSSPseudoElementType::backdrop,
aFrame->StyleContext()->AsServo(),
/* aPseudoElement = */ nullptr);
// NOTE(emilio): We can't use the changes handled for the owner of the
// backdrop frame, since it's out of flow, and parented to the viewport frame.
MOZ_ASSERT(backdropFrame->GetParent()->IsViewportFrame());
- ServoRestyleState state(aStyleSet, aChangeList);
+ nsTArray<nsIFrame*> wrappersToRestyle;
+ ServoRestyleState state(aStyleSet, aChangeList, wrappersToRestyle);
aFrame->UpdateStyleOfOwnedChildFrame(backdropFrame, newContext, state);
}
static void
UpdateFirstLetterIfNeeded(nsIFrame* aFrame, ServoRestyleState& aRestyleState)
{
if (!aFrame->HasFirstLetterChild()) {
return;
@@ -540,25 +693,27 @@ NeedsToTraverseElementChildren(const Ele
return false;
}
bool
ServoRestyleManager::ProcessPostTraversal(
Element* aElement,
ServoStyleContext* aParentContext,
ServoRestyleState& aRestyleState,
- ServoTraversalFlags aFlags)
+ ServoTraversalFlags aFlags,
+ bool aParentWasRestyled)
{
nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(aElement);
+ nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
// NOTE(emilio): This is needed because for table frames the bit is set on the
// table wrapper (which is the primary frame), not on the table itself.
const bool isOutOfFlow =
- aElement->GetPrimaryFrame() &&
- aElement->GetPrimaryFrame()->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
+ primaryFrame &&
+ primaryFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
// Grab the change hint from Servo.
// In case of flushing throttled animations, any restyle hints other than
// animations are preserved since they are the hints which will be processed
// in normal restyle later.
bool wasRestyled;
nsChangeHint changeHint = Servo_TakeChangeHint(aElement,
aFlags,
@@ -573,19 +728,34 @@ ServoRestyleManager::ProcessPostTraversa
// Handle lazy frame construction by posting a reconstruct for any lazily-
// constructed roots.
if (aElement->HasFlag(NODE_NEEDS_FRAME)) {
changeHint |= nsChangeHint_ReconstructFrame;
MOZ_ASSERT(!styleFrame);
}
- if (styleFrame && !isOutOfFlow) {
- changeHint = NS_RemoveSubsumedHints(
- changeHint, aRestyleState.ChangesHandledFor(*styleFrame));
+ if (styleFrame) {
+ MOZ_ASSERT(primaryFrame);
+
+ nsIFrame* maybeAnonBoxChild;
+ if (isOutOfFlow) {
+ maybeAnonBoxChild = primaryFrame->GetPlaceholderFrame();
+ } else {
+ maybeAnonBoxChild = primaryFrame;
+ changeHint = NS_RemoveSubsumedHints(
+ changeHint, aRestyleState.ChangesHandledFor(*styleFrame));
+ }
+
+ // If the parent wasn't restyled, the styles of our anon box parents won't
+ // change either.
+ if (aParentWasRestyled && maybeAnonBoxChild->ParentIsWrapperAnonBox()) {
+ aRestyleState.AddPendingWrapperRestyle(
+ ServoRestyleState::TableAwareParentFor(maybeAnonBoxChild));
+ }
}
// Although we shouldn't generate non-ReconstructFrame hints for elements with
// no frames, we can still get them here if they were explicitly posted by
// PostRestyleEvent, such as a RepaintFrame hint when a :link changes to be
// :visited. Skip processing these hints if there is no frame.
if ((styleFrame || (changeHint & nsChangeHint_ReconstructFrame)) && changeHint) {
aRestyleState.ChangeList().AppendChange(styleFrame, aElement, changeHint);
@@ -709,28 +879,34 @@ ServoRestyleManager::ProcessPostTraversa
TextPostTraversalState textState(*upToDateContext,
displayContentsNode && wasRestyled,
childrenRestyleState);
for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
if (traverseElementChildren && n->IsElement()) {
recreatedAnyContext |= ProcessPostTraversal(n->AsElement(),
upToDateContext,
childrenRestyleState,
- aFlags);
+ aFlags,
+ wasRestyled);
} else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) {
- recreatedAnyContext |= ProcessPostTraversalForText(n, textState);
+ recreatedAnyContext |= ProcessPostTraversalForText(n, textState,
+ childrenRestyleState,
+ wasRestyled);
}
}
}
// We want to update frame pseudo-element styles after we've traversed our
// kids, because some of those updates (::first-line/::first-letter) need to
// modify the styles of the kids, and the child traversal above would just
// clobber those modifications.
if (styleFrame) {
+ // Process anon box wrapper frames before ::first-line bits.
+ childrenRestyleState.ProcessWrapperRestyles(styleFrame);
+
if (wasRestyled) {
UpdateFramePseudoElementStyles(styleFrame, childrenRestyleState);
} else if (traverseElementChildren &&
styleFrame->IsFrameOfType(nsIFrame::eBlockFrame)) {
// Even if we were not restyled, if we're a block with a first-line and
// one of our descendant elements which is on the first line was restyled,
// we need to update the styles of things on the first line, because
// they're wrong now.
@@ -751,37 +927,47 @@ ServoRestyleManager::ProcessPostTraversa
}
}
if (!forThrottledAnimationFlush) {
aElement->UnsetHasDirtyDescendantsForServo();
aElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
}
aElement->UnsetHasAnimationOnlyDirtyDescendantsForServo();
+
return recreatedAnyContext;
}
bool
ServoRestyleManager::ProcessPostTraversalForText(
nsIContent* aTextNode,
- TextPostTraversalState& aPostTraversalState)
+ TextPostTraversalState& aPostTraversalState,
+ ServoRestyleState& aRestyleState,
+ bool aParentWasRestyled)
{
// Handle lazy frame construction.
if (aTextNode->HasFlag(NODE_NEEDS_FRAME)) {
aPostTraversalState.ChangeList().AppendChange(
nullptr, aTextNode, nsChangeHint_ReconstructFrame);
return true;
}
// Handle restyle.
nsIFrame* primaryFrame = aTextNode->GetPrimaryFrame();
if (!primaryFrame) {
return false;
}
+ // If the parent wasn't restyled, the styles of our anon box parents won't
+ // change either.
+ if (aParentWasRestyled && primaryFrame->ParentIsWrapperAnonBox()) {
+ aRestyleState.AddPendingWrapperRestyle(
+ ServoRestyleState::TableAwareParentFor(primaryFrame));
+ }
+
nsStyleContext& newContext = aPostTraversalState.ComputeStyle(aTextNode);
aPostTraversalState.ComputeHintIfNeeded(aTextNode, primaryFrame, newContext);
// We want to walk all the continuations here, even the ones with different
// styles. In practice, the only reasons we get continuations with different
// styles are ::first-line and ::first-letter. But in those cases,
// newContext is the right context for the _later_ continuations anyway (the
// ones not affected by ::first-line/::first-letter), not the earlier ones,
@@ -916,19 +1102,21 @@ ServoRestyleManager::DoProcessPendingRes
bool anyStyleChanged = false;
// Recreate style contexts, and queue up change hints (which also handle
// lazy frame construction).
{
AutoRestyleTimelineMarker marker(mPresContext->GetDocShell(), forThrottledAnimationFlush);
DocumentStyleRootIterator iter(doc);
while (Element* root = iter.GetNextStyleRoot()) {
- ServoRestyleState state(*styleSet, currentChanges);
+ nsTArray<nsIFrame*> wrappersToRestyle;
+ ServoRestyleState state(*styleSet, currentChanges, wrappersToRestyle);
anyStyleChanged |=
- ProcessPostTraversal(root, nullptr, state, aFlags);
+ ProcessPostTraversal(root, nullptr, state, aFlags,
+ /* aParentWasRestyled = */ false);
}
}
// Process the change hints.
//
// Unfortunately, the frame constructor can generate new change hints while
// processing existing ones. We redirect those into a secondary queue and
// iterate until there's nothing left.
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.h
@@ -31,78 +31,141 @@ namespace mozilla {
/**
* A stack class used to pass some common restyle state in a slightly more
* comfortable way than a bunch of individual arguments, and that also checks
* that the change hint used for optimization is correctly used in debug mode.
*/
class ServoRestyleState
{
public:
- ServoRestyleState(ServoStyleSet& aStyleSet, nsStyleChangeList& aChangeList)
+ ServoRestyleState(ServoStyleSet& aStyleSet, nsStyleChangeList& aChangeList,
+ nsTArray<nsIFrame*>& aPendingWrapperRestyles)
: mStyleSet(aStyleSet)
, mChangeList(aChangeList)
+ , mPendingWrapperRestyles(aPendingWrapperRestyles)
+ , mPendingWrapperRestyleOffset(aPendingWrapperRestyles.Length())
, mChangesHandled(nsChangeHint(0))
{}
// We shouldn't assume that changes handled from our parent are handled for
// our children too if we're out of flow since they aren't necessarily
// parented in DOM order, and thus a change handled by a DOM ancestor doesn't
// necessarily mean that it's handled for an ancestor frame.
enum class Type
{
InFlow,
OutOfFlow,
};
ServoRestyleState(const nsIFrame& aOwner,
ServoRestyleState& aParentState,
nsChangeHint aHintForThisFrame,
- Type aType)
+ Type aType,
+ bool aAssertWrapperRestyleLength = true)
: mStyleSet(aParentState.mStyleSet)
, mChangeList(aParentState.mChangeList)
+ , mPendingWrapperRestyles(aParentState.mPendingWrapperRestyles)
+ , mPendingWrapperRestyleOffset(aParentState.mPendingWrapperRestyles.Length())
, mChangesHandled(
aType == Type::InFlow
? aParentState.mChangesHandled | aHintForThisFrame
: aHintForThisFrame)
#ifdef DEBUG
, mOwner(&aOwner)
+ , mAssertWrapperRestyleLength(aAssertWrapperRestyleLength)
#endif
{
if (aType == Type::InFlow) {
AssertOwner(aParentState);
}
}
+ ~ServoRestyleState() {
+ MOZ_ASSERT(!mAssertWrapperRestyleLength ||
+ mPendingWrapperRestyles.Length() == mPendingWrapperRestyleOffset,
+ "Someone forgot to call ProcessWrapperRestyles!");
+ }
+
nsStyleChangeList& ChangeList() { return mChangeList; }
ServoStyleSet& StyleSet() { return mStyleSet; }
#ifdef DEBUG
void AssertOwner(const ServoRestyleState& aParentState) const;
nsChangeHint ChangesHandledFor(const nsIFrame&) const;
#else
void AssertOwner(const ServoRestyleState&) const {}
nsChangeHint ChangesHandledFor(const nsIFrame&) const
{
return mChangesHandled;
}
#endif
+ // Add a pending wrapper restyle. We don't have to do anything if the thing
+ // being added is already last in the list, but otherwise we do want to add
+ // it, in order for ProcessWrapperRestyles to work correctly.
+ void AddPendingWrapperRestyle(nsIFrame* aWrapperFrame);
+
+ // Process wrapper restyles for this restyle state. This should be done
+ // before it comes off the stack.
+ void ProcessWrapperRestyles(nsIFrame* aParentFrame);
+
+ // Get the table-aware parent for the given child. This will walk through
+ // outer table and cellcontent frames.
+ static nsIFrame* TableAwareParentFor(const nsIFrame* aChild);
+
private:
+ // Process a wrapper restyle at the given index, and restyles for any
+ // wrappers nested in it. Returns the number of entries from
+ // mPendingWrapperRestyles that we processed. The return value is always at
+ // least 1.
+ size_t ProcessMaybeNestedWrapperRestyle(nsIFrame* aParent, size_t aIndex);
+
ServoStyleSet& mStyleSet;
nsStyleChangeList& mChangeList;
+
+ // A list of pending wrapper restyles. Anonymous box wrapper frames that need
+ // restyling are added to this list when their non-anonymous kids are
+ // restyled. This avoids us having to do linear searches along the frame tree
+ // for these anonymous boxes. The problem then becomes that we can have
+ // multiple kids all with the same anonymous parent, and we don't want to
+ // restyle it more than once. We use mPendingWrapperRestyles to track which
+ // anonymous wrapper boxes we've requested be restyled and which of them have
+ // already been restyled. We use a single array propagated through
+ // ServoRestyleStates by reference, because in a situation like this:
+ //
+ // <div style="display: table"><span></span></div>
+ //
+ // We have multiple wrappers to restyle (cell, row, table-row-group) and we
+ // want to add them in to the list all at once but restyle them using
+ // different ServoRestyleStates with different owners. When this situation
+ // occurs, the relevant frames will be placed in the array with ancestors
+ // before descendants.
+ nsTArray<nsIFrame*>& mPendingWrapperRestyles;
+
+ // Since we're given a possibly-nonempty mPendingWrapperRestyles to start
+ // with, we need to keep track of where the part of it we're responsible for
+ // starts.
+ size_t mPendingWrapperRestyleOffset;
+
const nsChangeHint mChangesHandled;
// We track the "owner" frame of this restyle state, that is, the frame that
// generated the last change that is stored in mChangesHandled, in order to
// verify that we only use mChangesHandled for actual descendants of that
// frame (given DOM order isn't always frame order, and that there are a few
// special cases for stuff like wrapper frames, ::backdrop, and so on).
#ifdef DEBUG
const nsIFrame* mOwner { nullptr };
#endif
+
+ // Whether we should assert in our destructor that we've processed all of the
+ // relevant wrapper restyles.
+#ifdef DEBUG
+ const bool mAssertWrapperRestyleLength = true;
+#endif // DEBUG
};
/**
* Restyle manager for a Servo-backed style system.
*/
class ServoRestyleManager : public RestyleManager
{
friend class ServoStyleSet;
@@ -202,21 +265,24 @@ private:
* Returns whether any style did actually change. There may be cases where we
* didn't need to change any style after all, for example, when a content
* attribute changes that happens not to have any effect on the style of that
* element or any descendant or sibling.
*/
bool ProcessPostTraversal(Element* aElement,
ServoStyleContext* aParentContext,
ServoRestyleState& aRestyleState,
- ServoTraversalFlags aFlags);
+ ServoTraversalFlags aFlags,
+ bool aParentWasRestyled);
struct TextPostTraversalState;
bool ProcessPostTraversalForText(nsIContent* aTextNode,
- TextPostTraversalState& aState);
+ TextPostTraversalState& aState,
+ ServoRestyleState& aRestyleState,
+ bool aParentWasRestyled);
inline ServoStyleSet* StyleSet() const
{
MOZ_ASSERT(PresContext()->StyleSet()->IsServo(),
"ServoRestyleManager should only be used with a Servo-flavored "
"style backend");
return PresContext()->StyleSet()->AsServo();
}
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1388625-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<style>
+ div::first-line { color: green }
+</style>
+<body style="width: 100px" onload="document.body.style.width = 'auto'">
+ <div>
+ <span style="display: ruby-base-container">Some</span>
+ <span style="display: ruby-base-container">text that is fairly long</span>
+ </div>
+</body>
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -488,8 +488,9 @@ load 1308793.svg
load 1308848-1.html
load 1308848-2.html
load 1338772-1.html
load 1343937.html
asserts(0-1) load 1343606.html # bug 1343948
load 1352380.html
load 1362423-1.html
load 1381323.html
+asserts-if(!stylo,1) load 1388625-1.html # bug 1389286
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -10205,17 +10205,24 @@ nsFrame::BoxMetrics() const
NS_ASSERTION(metrics, "A box layout method was called but InitBoxMetrics was never called");
return metrics;
}
void
nsIFrame::UpdateStyleOfChildAnonBox(nsIFrame* aChildFrame,
ServoRestyleState& aRestyleState)
{
- MOZ_ASSERT(aChildFrame->GetParent() == this,
+ MOZ_ASSERT(aChildFrame->GetParent() == this ||
+ (aChildFrame->IsTableFrame() &&
+ aChildFrame->GetParent()->GetParent() == this) ||
+ (aChildFrame->GetParent()->IsLineFrame() &&
+ aChildFrame->GetParent()->GetParent() == this) ||
+ (aChildFrame->IsTableFrame() &&
+ aChildFrame->GetParent()->GetParent()->IsLineFrame() &&
+ aChildFrame->GetParent()->GetParent()->GetParent() == this),
"This should only be used for children!");
MOZ_ASSERT(!GetContent() || !aChildFrame->GetContent() ||
aChildFrame->GetContent() == GetContent(),
"What content node is it a frame for?");
MOZ_ASSERT(!aChildFrame->GetPrevContinuation(),
"Only first continuations should end up here");
// We could force the caller to pass in the pseudo, since some callers know it
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -3342,16 +3342,19 @@ protected:
// owned by this frame, and then updates styles on each of them.
void DoUpdateStyleOfOwnedAnonBoxes(mozilla::ServoRestyleState& aRestyleState);
// A helper for DoUpdateStyleOfOwnedAnonBoxes for the specific case
// of the owned anon box being a child of this frame.
void UpdateStyleOfChildAnonBox(nsIFrame* aChildFrame,
mozilla::ServoRestyleState& aRestyleState);
+ // Allow ServoRestyleState to call UpdateStyleOfChildAnonBox.
+ friend class mozilla::ServoRestyleState;
+
public:
// A helper both for UpdateStyleOfChildAnonBox, and to update frame-backed
// pseudo-elements in ServoRestyleManager.
//
// This gets a style context that will be the new style context for
// `aChildFrame`, and takes care of updating it, calling CalcStyleDifference,
// and adding to the change list as appropriate.
//