|
Packit |
f0b94e |
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
Packit |
f0b94e |
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
Packit |
f0b94e |
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
Packit |
f0b94e |
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
Packit |
f0b94e |
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
#include "mozilla/ServoRestyleManager.h"
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
#include "mozilla/AutoRestyleTimelineMarker.h"
|
|
Packit |
f0b94e |
#include "mozilla/AutoTimelineMarker.h"
|
|
Packit |
f0b94e |
#include "mozilla/DocumentStyleRootIterator.h"
|
|
Packit |
f0b94e |
#include "mozilla/ServoBindings.h"
|
|
Packit |
f0b94e |
#include "mozilla/ServoStyleSet.h"
|
|
Packit |
f0b94e |
#include "mozilla/ServoStyleContext.h"
|
|
Packit |
f0b94e |
#include "mozilla/ServoStyleContextInlines.h"
|
|
Packit |
f0b94e |
#include "mozilla/Unused.h"
|
|
Packit |
f0b94e |
#include "mozilla/ViewportFrame.h"
|
|
Packit |
f0b94e |
#include "mozilla/dom/ChildIterator.h"
|
|
Packit |
f0b94e |
#include "mozilla/dom/ElementInlines.h"
|
|
Packit |
f0b94e |
#include "nsBlockFrame.h"
|
|
Packit |
f0b94e |
#include "nsBulletFrame.h"
|
|
Packit |
f0b94e |
#include "nsIFrameInlines.h"
|
|
Packit |
f0b94e |
#include "nsImageFrame.h"
|
|
Packit |
f0b94e |
#include "nsPlaceholderFrame.h"
|
|
Packit |
f0b94e |
#include "nsContentUtils.h"
|
|
Packit |
f0b94e |
#include "nsCSSFrameConstructor.h"
|
|
Packit |
f0b94e |
#include "nsPrintfCString.h"
|
|
Packit |
f0b94e |
#include "nsRefreshDriver.h"
|
|
Packit |
f0b94e |
#include "nsStyleChangeList.h"
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
#ifdef ACCESSIBILITY
|
|
Packit |
f0b94e |
#include "nsAccessibilityService.h"
|
|
Packit |
f0b94e |
#endif
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
using namespace mozilla::dom;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
namespace mozilla {
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
#ifdef DEBUG
|
|
Packit |
f0b94e |
static bool IsAnonBox(const nsIFrame& aFrame) {
|
|
Packit |
f0b94e |
return aFrame.StyleContext()->IsAnonBox();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
static const nsIFrame* FirstContinuationOrPartOfIBSplit(
|
|
Packit |
f0b94e |
const nsIFrame* aFrame) {
|
|
Packit |
f0b94e |
if (!aFrame) {
|
|
Packit |
f0b94e |
return nullptr;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
return nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
static const nsIFrame* ExpectedOwnerForChild(const nsIFrame& aFrame) {
|
|
Packit |
f0b94e |
const nsIFrame* parent = aFrame.GetParent();
|
|
Packit |
f0b94e |
if (aFrame.IsTableFrame()) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(parent->IsTableWrapperFrame());
|
|
Packit |
f0b94e |
parent = parent->GetParent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (IsAnonBox(aFrame) && !aFrame.IsTextFrame()) {
|
|
Packit |
f0b94e |
if (parent->IsLineFrame()) {
|
|
Packit |
f0b94e |
parent = parent->GetParent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
return parent->IsViewportFrame() ? nullptr
|
|
Packit |
f0b94e |
: FirstContinuationOrPartOfIBSplit(parent);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (aFrame.IsBulletFrame()) {
|
|
Packit |
f0b94e |
return FirstContinuationOrPartOfIBSplit(parent);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (aFrame.IsLineFrame()) {
|
|
Packit |
f0b94e |
// A ::first-line always ends up here via its block, which is therefore the
|
|
Packit |
f0b94e |
// right expected owner. That block can be an
|
|
Packit |
f0b94e |
// anonymous box. For example, we could have a ::first-line on a columnated
|
|
Packit |
f0b94e |
// block; the blockframe is the column-content anonymous box in that case.
|
|
Packit |
f0b94e |
// So we don't want to end up in the code below, which steps out of anon
|
|
Packit |
f0b94e |
// boxes. Just return the parent of the line frame, which is the block.
|
|
Packit |
f0b94e |
return parent;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (aFrame.IsLetterFrame()) {
|
|
Packit |
f0b94e |
// Ditto for ::first-letter. A first-letter always arrives here via its
|
|
Packit |
f0b94e |
// direct parent, except when it's parented to a ::first-line.
|
|
Packit |
f0b94e |
if (parent->IsLineFrame()) {
|
|
Packit |
f0b94e |
parent = parent->GetParent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
return FirstContinuationOrPartOfIBSplit(parent);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (parent->IsLetterFrame()) {
|
|
Packit |
f0b94e |
// Things never have ::first-letter as their expected parent. Go
|
|
Packit |
f0b94e |
// on up to the ::first-letter's parent.
|
|
Packit |
f0b94e |
parent = parent->GetParent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
parent = FirstContinuationOrPartOfIBSplit(parent);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// We've handled already anon boxes and bullet frames, so now we're looking at
|
|
Packit |
f0b94e |
// a frame of a DOM element or pseudo. Hop through anon and line-boxes
|
|
Packit |
f0b94e |
// generated by our DOM parent, and go find the owner frame for it.
|
|
Packit |
f0b94e |
while (parent && (IsAnonBox(*parent) || parent->IsLineFrame())) {
|
|
Packit |
f0b94e |
auto* pseudo = parent->StyleContext()->GetPseudo();
|
|
Packit |
f0b94e |
if (pseudo == nsCSSAnonBoxes::tableWrapper) {
|
|
Packit |
f0b94e |
const nsIFrame* tableFrame = parent->PrincipalChildList().FirstChild();
|
|
Packit |
f0b94e |
MOZ_ASSERT(tableFrame->IsTableFrame());
|
|
Packit |
f0b94e |
// Handle :-moz-table and :-moz-inline-table.
|
|
Packit |
f0b94e |
parent = IsAnonBox(*tableFrame) ? parent->GetParent() : tableFrame;
|
|
Packit |
f0b94e |
} else {
|
|
Packit |
f0b94e |
// We get the in-flow parent here so that we can handle the OOF anonymous
|
|
Packit |
f0b94e |
// boxed to get the correct parent.
|
|
Packit |
f0b94e |
parent = parent->GetInFlowParent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
parent = FirstContinuationOrPartOfIBSplit(parent);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
return parent;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleState::AssertOwner(const ServoRestyleState& aParent) const {
|
|
Packit |
f0b94e |
MOZ_ASSERT(mOwner);
|
|
Packit |
f0b94e |
MOZ_ASSERT(!mOwner->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));
|
|
Packit |
f0b94e |
// We allow aParent.mOwner to be null, for cases when we're not starting at
|
|
Packit |
f0b94e |
// the root of the tree. We also allow aParent.mOwner to be somewhere up our
|
|
Packit |
f0b94e |
// expected owner chain not our immediate owner, which allows us creating long
|
|
Packit |
f0b94e |
// chains of ServoRestyleStates in some cases where it's just not worth it.
|
|
Packit |
f0b94e |
#ifdef DEBUG
|
|
Packit |
f0b94e |
if (aParent.mOwner) {
|
|
Packit |
f0b94e |
const nsIFrame* owner = ExpectedOwnerForChild(*mOwner);
|
|
Packit |
f0b94e |
if (owner != aParent.mOwner) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(IsAnonBox(*owner),
|
|
Packit |
f0b94e |
"Should only have expected owner weirdness when anon boxes "
|
|
Packit |
f0b94e |
"are involved");
|
|
Packit |
f0b94e |
bool found = false;
|
|
Packit |
f0b94e |
for (; owner; owner = ExpectedOwnerForChild(*owner)) {
|
|
Packit |
f0b94e |
if (owner == aParent.mOwner) {
|
|
Packit |
f0b94e |
found = true;
|
|
Packit |
f0b94e |
break;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
MOZ_ASSERT(found, "Must have aParent.mOwner on our expected owner chain");
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
#endif
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsChangeHint ServoRestyleState::ChangesHandledFor(
|
|
Packit |
f0b94e |
const nsIFrame& aFrame) const {
|
|
Packit |
f0b94e |
if (!mOwner) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(!mChangesHandled);
|
|
Packit |
f0b94e |
return mChangesHandled;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
MOZ_ASSERT(mOwner == ExpectedOwnerForChild(aFrame),
|
|
Packit |
f0b94e |
"Missed some frame in the hierarchy?");
|
|
Packit |
f0b94e |
return mChangesHandled;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
#endif
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleState::AddPendingWrapperRestyle(nsIFrame* aWrapperFrame) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(aWrapperFrame->StyleContext()->IsWrapperAnonBox(),
|
|
Packit |
f0b94e |
"All our wrappers are anon boxes, and why would we restyle "
|
|
Packit |
f0b94e |
"non-inheriting ones?");
|
|
Packit |
f0b94e |
MOZ_ASSERT(aWrapperFrame->StyleContext()->IsInheritingAnonBox(),
|
|
Packit |
f0b94e |
"All our wrappers are anon boxes, and why would we restyle "
|
|
Packit |
f0b94e |
"non-inheriting ones?");
|
|
Packit |
f0b94e |
MOZ_ASSERT(
|
|
Packit |
f0b94e |
aWrapperFrame->StyleContext()->GetPseudo() != nsCSSAnonBoxes::cellContent,
|
|
Packit |
f0b94e |
"Someone should be using TableAwareParentFor");
|
|
Packit |
f0b94e |
MOZ_ASSERT(aWrapperFrame->StyleContext()->GetPseudo() !=
|
|
Packit |
f0b94e |
nsCSSAnonBoxes::tableWrapper,
|
|
Packit |
f0b94e |
"Someone should be using TableAwareParentFor");
|
|
Packit |
f0b94e |
// Make sure we only add first continuations.
|
|
Packit |
f0b94e |
aWrapperFrame = aWrapperFrame->FirstContinuation();
|
|
Packit |
f0b94e |
nsIFrame* last = mPendingWrapperRestyles.SafeLastElement(nullptr);
|
|
Packit |
f0b94e |
if (last == aWrapperFrame) {
|
|
Packit |
f0b94e |
// Already queued up, nothing to do.
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Make sure to queue up parents before children. But don't queue up
|
|
Packit |
f0b94e |
// ancestors of non-anonymous boxes here; those are handled when we traverse
|
|
Packit |
f0b94e |
// their non-anonymous kids.
|
|
Packit |
f0b94e |
if (aWrapperFrame->ParentIsWrapperAnonBox()) {
|
|
Packit |
f0b94e |
AddPendingWrapperRestyle(TableAwareParentFor(aWrapperFrame));
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// If the append fails, we'll fail to restyle properly, but that's probably
|
|
Packit |
f0b94e |
// better than crashing.
|
|
Packit |
f0b94e |
if (mPendingWrapperRestyles.AppendElement(aWrapperFrame, fallible)) {
|
|
Packit |
f0b94e |
aWrapperFrame->SetIsWrapperAnonBoxNeedingRestyle(true);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleState::ProcessWrapperRestyles(nsIFrame* aParentFrame) {
|
|
Packit |
f0b94e |
size_t i = mPendingWrapperRestyleOffset;
|
|
Packit |
f0b94e |
while (i < mPendingWrapperRestyles.Length()) {
|
|
Packit |
f0b94e |
i += ProcessMaybeNestedWrapperRestyle(aParentFrame, i);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
mPendingWrapperRestyles.TruncateLength(mPendingWrapperRestyleOffset);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
size_t ServoRestyleState::ProcessMaybeNestedWrapperRestyle(nsIFrame* aParent,
|
|
Packit |
f0b94e |
size_t aIndex) {
|
|
Packit |
f0b94e |
// The frame at index aIndex is something we should restyle ourselves, but
|
|
Packit |
f0b94e |
// following frames may need separate ServoRestyleStates to restyle.
|
|
Packit |
f0b94e |
MOZ_ASSERT(aIndex < mPendingWrapperRestyles.Length());
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsIFrame* cur = mPendingWrapperRestyles[aIndex];
|
|
Packit |
f0b94e |
MOZ_ASSERT(cur->StyleContext()->IsWrapperAnonBox());
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Where is cur supposed to inherit from? From its parent frame, except in
|
|
Packit |
f0b94e |
// the case when cur is a table, in which case it should be its grandparent.
|
|
Packit |
f0b94e |
// Also, not in the case when the resulting frame would be a first-line; in
|
|
Packit |
f0b94e |
// that case we should be inheriting from the block, and the first-line will
|
|
Packit |
f0b94e |
// do its fixup later if needed.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// Note that after we do all that fixup the parent we get might still not be
|
|
Packit |
f0b94e |
// aParent; for example aParent could be a scrollframe, in which case we
|
|
Packit |
f0b94e |
// should inherit from the scrollcontent frame. Or the parent might be some
|
|
Packit |
f0b94e |
// continuation of aParent.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// Try to assert as much as we can about the parent we actually end up using
|
|
Packit |
f0b94e |
// without triggering bogus asserts in all those various edge cases.
|
|
Packit |
f0b94e |
nsIFrame* parent = cur->GetParent();
|
|
Packit |
f0b94e |
if (cur->IsTableFrame()) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(parent->IsTableWrapperFrame());
|
|
Packit |
f0b94e |
parent = parent->GetParent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
if (parent->IsLineFrame()) {
|
|
Packit |
f0b94e |
parent = parent->GetParent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
MOZ_ASSERT(FirstContinuationOrPartOfIBSplit(parent) == aParent ||
|
|
Packit |
f0b94e |
(parent->StyleContext()->IsInheritingAnonBox() &&
|
|
Packit |
f0b94e |
parent->GetContent() == aParent->GetContent()));
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Now "this" is a ServoRestyleState for aParent, so if parent is not a next
|
|
Packit |
f0b94e |
// continuation (possibly across ib splits) of aParent we need a new
|
|
Packit |
f0b94e |
// ServoRestyleState for the kid.
|
|
Packit |
f0b94e |
Maybe<ServoRestyleState> parentRestyleState;
|
|
Packit |
f0b94e |
nsIFrame* parentForRestyle =
|
|
Packit |
f0b94e |
nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent);
|
|
Packit |
f0b94e |
if (parentForRestyle != aParent) {
|
|
Packit |
f0b94e |
parentRestyleState.emplace(*parentForRestyle, *this, nsChangeHint_Empty,
|
|
Packit |
f0b94e |
Type::InFlow);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
ServoRestyleState& curRestyleState =
|
|
Packit |
f0b94e |
parentRestyleState ? *parentRestyleState : *this;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// This frame may already have been restyled. Even if it has, we can't just
|
|
Packit |
f0b94e |
// return, because the next frame may be a kid of it that does need restyling.
|
|
Packit |
f0b94e |
if (cur->IsWrapperAnonBoxNeedingRestyle()) {
|
|
Packit |
f0b94e |
parentForRestyle->UpdateStyleOfChildAnonBox(cur, curRestyleState);
|
|
Packit |
f0b94e |
cur->SetIsWrapperAnonBoxNeedingRestyle(false);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
size_t numProcessed = 1;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Note: no overflow possible here, since aIndex < length.
|
|
Packit |
f0b94e |
if (aIndex + 1 < mPendingWrapperRestyles.Length()) {
|
|
Packit |
f0b94e |
nsIFrame* next = mPendingWrapperRestyles[aIndex + 1];
|
|
Packit |
f0b94e |
if (TableAwareParentFor(next) == cur &&
|
|
Packit |
f0b94e |
next->IsWrapperAnonBoxNeedingRestyle()) {
|
|
Packit |
f0b94e |
// It might be nice if we could do better than nsChangeHint_Empty. On
|
|
Packit |
f0b94e |
// the other hand, presumably our mChangesHandled already has the bits
|
|
Packit |
f0b94e |
// we really want here so in practice it doesn't matter.
|
|
Packit |
f0b94e |
ServoRestyleState childState(*cur, curRestyleState, nsChangeHint_Empty,
|
|
Packit |
f0b94e |
Type::InFlow,
|
|
Packit |
f0b94e |
/* aAssertWrapperRestyleLength = */ false);
|
|
Packit |
f0b94e |
numProcessed +=
|
|
Packit |
f0b94e |
childState.ProcessMaybeNestedWrapperRestyle(cur, aIndex + 1);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
return numProcessed;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsIFrame* ServoRestyleState::TableAwareParentFor(const nsIFrame* aChild) {
|
|
Packit |
f0b94e |
// We want to get the anon box parent for aChild. where aChild has
|
|
Packit |
f0b94e |
// ParentIsWrapperAnonBox().
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// For the most part this is pretty straightforward, but there are two
|
|
Packit |
f0b94e |
// wrinkles. First, if aChild is a table, then we really want the parent of
|
|
Packit |
f0b94e |
// its table wrapper.
|
|
Packit |
f0b94e |
if (aChild->IsTableFrame()) {
|
|
Packit |
f0b94e |
aChild = aChild->GetParent();
|
|
Packit |
f0b94e |
MOZ_ASSERT(aChild->IsTableWrapperFrame());
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsIFrame* parent = aChild->GetParent();
|
|
Packit |
f0b94e |
// Now if parent is a cell-content frame, we actually want the cellframe.
|
|
Packit |
f0b94e |
if (parent->StyleContext()->GetPseudo() == nsCSSAnonBoxes::cellContent) {
|
|
Packit |
f0b94e |
parent = parent->GetParent();
|
|
Packit |
f0b94e |
} else if (parent->IsTableWrapperFrame()) {
|
|
Packit |
f0b94e |
// Must be a caption. In that case we want the table here.
|
|
Packit |
f0b94e |
MOZ_ASSERT(aChild->StyleDisplay()->mDisplay == StyleDisplay::TableCaption);
|
|
Packit |
f0b94e |
parent = parent->PrincipalChildList().FirstChild();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
return parent;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ServoRestyleManager::ServoRestyleManager(nsPresContext* aPresContext)
|
|
Packit |
f0b94e |
: RestyleManager(StyleBackendType::Servo, aPresContext),
|
|
Packit |
f0b94e |
mReentrantChanges(nullptr) {}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleManager::PostRestyleEvent(Element* aElement,
|
|
Packit |
f0b94e |
nsRestyleHint aRestyleHint,
|
|
Packit |
f0b94e |
nsChangeHint aMinChangeHint) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(!(aMinChangeHint & nsChangeHint_NeutralChange),
|
|
Packit |
f0b94e |
"Didn't expect explicit change hints to be neutral!");
|
|
Packit |
f0b94e |
if (MOZ_UNLIKELY(IsDisconnected()) ||
|
|
Packit |
f0b94e |
MOZ_UNLIKELY(PresContext()->PresShell()->IsDestroying())) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// We allow posting restyles from within change hint handling, but not from
|
|
Packit |
f0b94e |
// within the restyle algorithm itself.
|
|
Packit |
f0b94e |
MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (aRestyleHint == 0 && !aMinChangeHint) {
|
|
Packit |
f0b94e |
return; // Nothing to do.
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Assuming the restyle hints will invalidate cached style for
|
|
Packit |
f0b94e |
// getComputedStyle, since we don't know if any of the restyling that we do
|
|
Packit |
f0b94e |
// would affect undisplayed elements.
|
|
Packit |
f0b94e |
if (aRestyleHint) {
|
|
Packit |
f0b94e |
IncrementUndisplayedRestyleGeneration();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Processing change hints sometimes causes new change hints to be generated,
|
|
Packit |
f0b94e |
// and very occasionally, additional restyle hints. We collect the change
|
|
Packit |
f0b94e |
// hints manually to avoid re-traversing the DOM to find them.
|
|
Packit |
f0b94e |
if (mReentrantChanges && !aRestyleHint) {
|
|
Packit |
f0b94e |
mReentrantChanges->AppendElement(ReentrantChange{aElement, aMinChangeHint});
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (aRestyleHint & ~eRestyle_AllHintsWithAnimations) {
|
|
Packit |
f0b94e |
mHaveNonAnimationRestyles = true;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (aRestyleHint & eRestyle_LaterSiblings) {
|
|
Packit |
f0b94e |
aRestyleHint &= ~eRestyle_LaterSiblings;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsRestyleHint siblingHint = eRestyle_Subtree;
|
|
Packit |
f0b94e |
Element* current = aElement->GetNextElementSibling();
|
|
Packit |
f0b94e |
while (current) {
|
|
Packit |
f0b94e |
Servo_NoteExplicitHints(current, siblingHint, nsChangeHint(0));
|
|
Packit |
f0b94e |
current = current->GetNextElementSibling();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (aRestyleHint || aMinChangeHint) {
|
|
Packit |
f0b94e |
Servo_NoteExplicitHints(aElement, aRestyleHint, aMinChangeHint);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleManager::PostRestyleEventForCSSRuleChanges() {
|
|
Packit |
f0b94e |
mRestyleForCSSRuleChanges = true;
|
|
Packit |
f0b94e |
mPresContext->PresShell()->EnsureStyleFlush();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleManager::PostRestyleEventForAnimations(
|
|
Packit |
f0b94e |
Element* aElement, CSSPseudoElementType aPseudoType,
|
|
Packit |
f0b94e |
nsRestyleHint aRestyleHint) {
|
|
Packit |
f0b94e |
Element* elementToRestyle =
|
|
Packit |
f0b94e |
EffectCompositor::GetElementToRestyle(aElement, aPseudoType);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (!elementToRestyle) {
|
|
Packit |
f0b94e |
// FIXME: Bug 1371107: When reframing happens,
|
|
Packit |
f0b94e |
// EffectCompositor::mElementsToRestyle still has unbinded old pseudo
|
|
Packit |
f0b94e |
// element. We should drop it.
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
AutoRestyleTimelineMarker marker(mPresContext->GetDocShell(),
|
|
Packit |
f0b94e |
true /* animation-only */);
|
|
Packit |
f0b94e |
Servo_NoteExplicitHints(elementToRestyle, aRestyleHint, nsChangeHint(0));
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint,
|
|
Packit |
f0b94e |
nsRestyleHint aRestyleHint) {
|
|
Packit |
f0b94e |
// NOTE(emilio): GeckoRestlyeManager does a sync style flush, which seems not
|
|
Packit |
f0b94e |
// to be needed in my testing.
|
|
Packit |
f0b94e |
PostRebuildAllStyleDataEvent(aExtraHint, aRestyleHint);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleManager::PostRebuildAllStyleDataEvent(
|
|
Packit |
f0b94e |
nsChangeHint aExtraHint, nsRestyleHint aRestyleHint) {
|
|
Packit |
f0b94e |
// NOTE(emilio): The semantics of these methods are quite funny, in the sense
|
|
Packit |
f0b94e |
// that we're not supposed to need to rebuild the actual stylist data.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// That's handled as part of the MediumFeaturesChanged stuff, if needed.
|
|
Packit |
f0b94e |
StyleSet()->ClearCachedStyleData();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
DocumentStyleRootIterator iter(mPresContext->Document());
|
|
Packit |
f0b94e |
while (Element* root = iter.GetNextStyleRoot()) {
|
|
Packit |
f0b94e |
PostRestyleEvent(root, aRestyleHint, aExtraHint);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// TODO(emilio, bz): Extensions can add/remove stylesheets that can affect
|
|
Packit |
f0b94e |
// non-inheriting anon boxes. It's not clear if we want to support that, but
|
|
Packit |
f0b94e |
// if we do, we need to re-selector-match them here.
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/* static */ void ServoRestyleManager::ClearServoDataFromSubtree(
|
|
Packit |
f0b94e |
Element* aElement, IncludeRoot aIncludeRoot) {
|
|
Packit |
f0b94e |
if (aElement->HasServoData()) {
|
|
Packit |
f0b94e |
StyleChildrenIterator it(aElement);
|
|
Packit |
f0b94e |
for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
|
|
Packit |
f0b94e |
if (n->IsElement()) {
|
|
Packit |
f0b94e |
ClearServoDataFromSubtree(n->AsElement(), IncludeRoot::Yes);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (MOZ_LIKELY(aIncludeRoot == IncludeRoot::Yes)) {
|
|
Packit |
f0b94e |
aElement->ClearServoData();
|
|
Packit |
f0b94e |
MOZ_ASSERT(!aElement->HasAnyOfFlags(Element::kAllServoDescendantBits |
|
|
Packit |
f0b94e |
NODE_NEEDS_FRAME));
|
|
Packit |
f0b94e |
MOZ_ASSERT(aElement != aElement->OwnerDoc()->GetServoRestyleRoot());
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/* static */ void ServoRestyleManager::ClearRestyleStateFromSubtree(
|
|
Packit |
f0b94e |
Element* aElement) {
|
|
Packit |
f0b94e |
if (aElement->HasAnyOfFlags(Element::kAllServoDescendantBits)) {
|
|
Packit |
f0b94e |
StyleChildrenIterator it(aElement);
|
|
Packit |
f0b94e |
for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
|
|
Packit |
f0b94e |
if (n->IsElement()) {
|
|
Packit |
f0b94e |
ClearRestyleStateFromSubtree(n->AsElement());
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
bool wasRestyled;
|
|
Packit |
f0b94e |
Unused << Servo_TakeChangeHint(aElement, &wasRestyled);
|
|
Packit |
f0b94e |
aElement->UnsetFlags(Element::kAllServoDescendantBits);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* This struct takes care of encapsulating some common state that text nodes may
|
|
Packit |
f0b94e |
* need to track during the post-traversal.
|
|
Packit |
f0b94e |
*
|
|
Packit |
f0b94e |
* This is currently used to properly compute change hints when the parent
|
|
Packit |
f0b94e |
* element of this node is a display: contents node, and also to avoid computing
|
|
Packit |
f0b94e |
* the style for text children more than once per element.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
struct ServoRestyleManager::TextPostTraversalState {
|
|
Packit |
f0b94e |
public:
|
|
Packit |
f0b94e |
TextPostTraversalState(Element& aParentElement,
|
|
Packit |
f0b94e |
ServoStyleContext* aParentContext,
|
|
Packit |
f0b94e |
bool aDisplayContentsParentStyleChanged,
|
|
Packit |
f0b94e |
ServoRestyleState& aParentRestyleState)
|
|
Packit |
f0b94e |
: mParentElement(aParentElement),
|
|
Packit |
f0b94e |
mParentContext(aParentContext),
|
|
Packit |
f0b94e |
mParentRestyleState(aParentRestyleState),
|
|
Packit |
f0b94e |
mStyle(nullptr),
|
|
Packit |
f0b94e |
mShouldPostHints(aDisplayContentsParentStyleChanged),
|
|
Packit |
f0b94e |
mShouldComputeHints(aDisplayContentsParentStyleChanged),
|
|
Packit |
f0b94e |
mComputedHint(nsChangeHint_Empty) {}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsStyleChangeList& ChangeList() { return mParentRestyleState.ChangeList(); }
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsStyleContext& ComputeStyle(nsIContent* aTextNode) {
|
|
Packit |
f0b94e |
if (!mStyle) {
|
|
Packit |
f0b94e |
mStyle = mParentRestyleState.StyleSet().ResolveStyleForText(
|
|
Packit |
f0b94e |
aTextNode, &ParentStyle());
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
MOZ_ASSERT(mStyle);
|
|
Packit |
f0b94e |
return *mStyle;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ComputeHintIfNeeded(nsIContent* aContent, nsIFrame* aTextFrame,
|
|
Packit |
f0b94e |
nsStyleContext& aNewContext) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(aTextFrame);
|
|
Packit |
f0b94e |
MOZ_ASSERT(aNewContext.GetPseudo() == nsCSSAnonBoxes::mozText);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (MOZ_LIKELY(!mShouldPostHints)) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ServoStyleContext* oldContext = aTextFrame->StyleContext()->AsServo();
|
|
Packit |
f0b94e |
MOZ_ASSERT(oldContext->GetPseudo() == nsCSSAnonBoxes::mozText);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// We rely on the fact that all the text children for the same element share
|
|
Packit |
f0b94e |
// style to avoid recomputing style differences for all of them.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// TODO(emilio): The above may not be true for ::first-{line,letter}, but
|
|
Packit |
f0b94e |
// we'll cross that bridge when we support those in stylo.
|
|
Packit |
f0b94e |
if (mShouldComputeHints) {
|
|
Packit |
f0b94e |
mShouldComputeHints = false;
|
|
Packit |
f0b94e |
uint32_t equalStructs, samePointerStructs;
|
|
Packit |
f0b94e |
mComputedHint = oldContext->CalcStyleDifference(
|
|
Packit |
f0b94e |
&aNewContext, &equalStructs, &samePointerStructs);
|
|
Packit |
f0b94e |
mComputedHint = NS_RemoveSubsumedHints(
|
|
Packit |
f0b94e |
mComputedHint, mParentRestyleState.ChangesHandledFor(*aTextFrame));
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (mComputedHint) {
|
|
Packit |
f0b94e |
mParentRestyleState.ChangeList().AppendChange(aTextFrame, aContent,
|
|
Packit |
f0b94e |
mComputedHint);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
private:
|
|
Packit |
f0b94e |
ServoStyleContext& ParentStyle() {
|
|
Packit |
f0b94e |
if (!mParentContext) {
|
|
Packit |
f0b94e |
mLazilyResolvedParentContext =
|
|
Packit |
f0b94e |
mParentRestyleState.StyleSet().ResolveServoStyle(&mParentElement);
|
|
Packit |
f0b94e |
mParentContext = mLazilyResolvedParentContext;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
return *mParentContext;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Element& mParentElement;
|
|
Packit |
f0b94e |
ServoStyleContext* mParentContext;
|
|
Packit |
f0b94e |
RefPtr<ServoStyleContext> mLazilyResolvedParentContext;
|
|
Packit |
f0b94e |
ServoRestyleState& mParentRestyleState;
|
|
Packit |
f0b94e |
RefPtr<nsStyleContext> mStyle;
|
|
Packit |
f0b94e |
bool mShouldPostHints;
|
|
Packit |
f0b94e |
bool mShouldComputeHints;
|
|
Packit |
f0b94e |
nsChangeHint mComputedHint;
|
|
Packit |
f0b94e |
};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
static void UpdateBackdropIfNeeded(nsIFrame* aFrame, ServoStyleSet& aStyleSet,
|
|
Packit |
f0b94e |
nsStyleChangeList& aChangeList) {
|
|
Packit |
f0b94e |
const nsStyleDisplay* display = aFrame->StyleContext()->StyleDisplay();
|
|
Packit |
f0b94e |
if (display->mTopLayer != NS_STYLE_TOP_LAYER_TOP) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Elements in the top layer are guaranteed to have absolute or fixed
|
|
Packit |
f0b94e |
// position per https://fullscreen.spec.whatwg.org/#new-stacking-layer.
|
|
Packit |
f0b94e |
MOZ_ASSERT(display->IsAbsolutelyPositionedStyle());
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsIFrame* backdropPlaceholder =
|
|
Packit |
f0b94e |
aFrame->GetChildList(nsIFrame::kBackdropList).FirstChild();
|
|
Packit |
f0b94e |
if (!backdropPlaceholder) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
MOZ_ASSERT(backdropPlaceholder->IsPlaceholderFrame());
|
|
Packit |
f0b94e |
nsIFrame* backdropFrame =
|
|
Packit |
f0b94e |
nsPlaceholderFrame::GetRealFrameForPlaceholder(backdropPlaceholder);
|
|
Packit |
f0b94e |
MOZ_ASSERT(backdropFrame->IsBackdropFrame());
|
|
Packit |
f0b94e |
MOZ_ASSERT(backdropFrame->StyleContext()->GetPseudoType() ==
|
|
Packit |
f0b94e |
CSSPseudoElementType::backdrop);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
RefPtr<nsStyleContext> newContext = aStyleSet.ResolvePseudoElementStyle(
|
|
Packit |
f0b94e |
aFrame->GetContent()->AsElement(), CSSPseudoElementType::backdrop,
|
|
Packit |
f0b94e |
aFrame->StyleContext()->AsServo(),
|
|
Packit |
f0b94e |
/* aPseudoElement = */ nullptr);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// NOTE(emilio): We can't use the changes handled for the owner of the
|
|
Packit |
f0b94e |
// backdrop frame, since it's out of flow, and parented to the viewport or
|
|
Packit |
f0b94e |
// canvas frame (depending on the `position` value).
|
|
Packit |
f0b94e |
MOZ_ASSERT(backdropFrame->GetParent()->IsViewportFrame() ||
|
|
Packit |
f0b94e |
backdropFrame->GetParent()->IsCanvasFrame());
|
|
Packit |
f0b94e |
nsTArray<nsIFrame*> wrappersToRestyle;
|
|
Packit |
f0b94e |
ServoRestyleState state(aStyleSet, aChangeList, wrappersToRestyle);
|
|
Packit |
f0b94e |
aFrame->UpdateStyleOfOwnedChildFrame(backdropFrame, newContext, state);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
static void UpdateFirstLetterIfNeeded(nsIFrame* aFrame,
|
|
Packit |
f0b94e |
ServoRestyleState& aRestyleState) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(
|
|
Packit |
f0b94e |
!aFrame->IsFrameOfType(nsIFrame::eBlockFrame),
|
|
Packit |
f0b94e |
"You're probably duplicating work with UpdatePseudoElementStyles!");
|
|
Packit |
f0b94e |
if (!aFrame->HasFirstLetterChild()) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// We need to find the block the first-letter is associated with so we can
|
|
Packit |
f0b94e |
// find the right element for the first-letter's style resolution. Might as
|
|
Packit |
f0b94e |
// well just delegate the whole thing to that block.
|
|
Packit |
f0b94e |
nsIFrame* block = aFrame->GetParent();
|
|
Packit |
f0b94e |
while (!block->IsFrameOfType(nsIFrame::eBlockFrame)) {
|
|
Packit |
f0b94e |
block = block->GetParent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
static_cast<nsBlockFrame*>(block->FirstContinuation())
|
|
Packit |
f0b94e |
->UpdateFirstLetterStyle(aRestyleState);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
static void UpdateOneAdditionalStyleContext(nsIFrame* aFrame, uint32_t aIndex,
|
|
Packit |
f0b94e |
ServoStyleContext& aOldContext,
|
|
Packit |
f0b94e |
ServoRestyleState& aRestyleState) {
|
|
Packit |
f0b94e |
auto pseudoType = aOldContext.GetPseudoType();
|
|
Packit |
f0b94e |
MOZ_ASSERT(pseudoType != CSSPseudoElementType::NotPseudo);
|
|
Packit |
f0b94e |
MOZ_ASSERT(
|
|
Packit |
f0b94e |
!nsCSSPseudoElements::PseudoElementSupportsUserActionState(pseudoType));
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
RefPtr<ServoStyleContext> newContext =
|
|
Packit |
f0b94e |
aRestyleState.StyleSet().ResolvePseudoElementStyle(
|
|
Packit |
f0b94e |
aFrame->GetContent()->AsElement(), pseudoType,
|
|
Packit |
f0b94e |
aFrame->StyleContext()->AsServo(),
|
|
Packit |
f0b94e |
/* aPseudoElement = */ nullptr);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
uint32_t equalStructs, samePointerStructs; // Not used, actually.
|
|
Packit |
f0b94e |
nsChangeHint childHint = aOldContext.CalcStyleDifference(
|
|
Packit |
f0b94e |
newContext, &equalStructs, &samePointerStructs);
|
|
Packit |
f0b94e |
if (!aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
|
|
Packit |
f0b94e |
childHint = NS_RemoveSubsumedHints(
|
|
Packit |
f0b94e |
childHint, aRestyleState.ChangesHandledFor(*aFrame));
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (childHint) {
|
|
Packit |
f0b94e |
if (childHint & nsChangeHint_ReconstructFrame) {
|
|
Packit |
f0b94e |
// If we generate a reconstruct here, remove any non-reconstruct hints we
|
|
Packit |
f0b94e |
// may have already generated for this content.
|
|
Packit |
f0b94e |
aRestyleState.ChangeList().PopChangesForContent(aFrame->GetContent());
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
aRestyleState.ChangeList().AppendChange(aFrame, aFrame->GetContent(),
|
|
Packit |
f0b94e |
childHint);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
aFrame->SetAdditionalStyleContext(aIndex, newContext);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
static void UpdateAdditionalStyleContexts(nsIFrame* aFrame,
|
|
Packit |
f0b94e |
ServoRestyleState& aRestyleState) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(aFrame);
|
|
Packit |
f0b94e |
MOZ_ASSERT(aFrame->GetContent() && aFrame->GetContent()->IsElement());
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// FIXME(emilio): Consider adding a bit or something to avoid the initial
|
|
Packit |
f0b94e |
// virtual call?
|
|
Packit |
f0b94e |
uint32_t index = 0;
|
|
Packit |
f0b94e |
while (auto* oldContext = aFrame->GetAdditionalStyleContext(index)) {
|
|
Packit |
f0b94e |
UpdateOneAdditionalStyleContext(aFrame, index++, *oldContext->AsServo(),
|
|
Packit |
f0b94e |
aRestyleState);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
static void UpdateFramePseudoElementStyles(nsIFrame* aFrame,
|
|
Packit |
f0b94e |
ServoRestyleState& aRestyleState) {
|
|
Packit |
f0b94e |
if (aFrame->IsFrameOfType(nsIFrame::eBlockFrame)) {
|
|
Packit |
f0b94e |
static_cast<nsBlockFrame*>(aFrame)->UpdatePseudoElementStyles(
|
|
Packit |
f0b94e |
aRestyleState);
|
|
Packit |
f0b94e |
} else {
|
|
Packit |
f0b94e |
UpdateFirstLetterIfNeeded(aFrame, aRestyleState);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
UpdateBackdropIfNeeded(aFrame, aRestyleState.StyleSet(),
|
|
Packit |
f0b94e |
aRestyleState.ChangeList());
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
enum class ServoPostTraversalFlags : uint32_t {
|
|
Packit |
f0b94e |
Empty = 0,
|
|
Packit |
f0b94e |
// Whether parent was restyled.
|
|
Packit |
f0b94e |
ParentWasRestyled = 1 << 0,
|
|
Packit |
f0b94e |
// Skip sending accessibility notifications for all descendants.
|
|
Packit |
f0b94e |
SkipA11yNotifications = 1 << 1,
|
|
Packit |
f0b94e |
// Always send accessibility notifications if the element is shown.
|
|
Packit |
f0b94e |
// The SkipA11yNotifications flag above overrides this flag.
|
|
Packit |
f0b94e |
SendA11yNotificationsIfShown = 1 << 2,
|
|
Packit |
f0b94e |
};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ServoPostTraversalFlags)
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Send proper accessibility notifications and return post traversal
|
|
Packit |
f0b94e |
// flags for kids.
|
|
Packit |
f0b94e |
static ServoPostTraversalFlags SendA11yNotifications(
|
|
Packit |
f0b94e |
nsPresContext* aPresContext, Element* aElement,
|
|
Packit |
f0b94e |
nsStyleContext* aOldStyleContext, nsStyleContext* aNewStyleContext,
|
|
Packit |
f0b94e |
ServoPostTraversalFlags aFlags) {
|
|
Packit |
f0b94e |
using Flags = ServoPostTraversalFlags;
|
|
Packit |
f0b94e |
MOZ_ASSERT(!(aFlags & Flags::SkipA11yNotifications) ||
|
|
Packit |
f0b94e |
!(aFlags & Flags::SendA11yNotificationsIfShown),
|
|
Packit |
f0b94e |
"The two a11y flags should never be set together");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
#ifdef ACCESSIBILITY
|
|
Packit |
f0b94e |
nsAccessibilityService* accService = GetAccService();
|
|
Packit |
f0b94e |
if (!accService) {
|
|
Packit |
f0b94e |
// If we don't have accessibility service, accessibility is not
|
|
Packit |
f0b94e |
// enabled. Just skip everything.
|
|
Packit |
f0b94e |
return Flags::Empty;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
if (aFlags & Flags::SkipA11yNotifications) {
|
|
Packit |
f0b94e |
// Propogate the skipping flag to descendants.
|
|
Packit |
f0b94e |
return Flags::SkipA11yNotifications;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
bool needsNotify = false;
|
|
Packit |
f0b94e |
bool isVisible = aNewStyleContext->StyleVisibility()->IsVisible();
|
|
Packit |
f0b94e |
if (aFlags & Flags::SendA11yNotificationsIfShown) {
|
|
Packit |
f0b94e |
if (!isVisible) {
|
|
Packit |
f0b94e |
// Propagate the sending-if-shown flag to descendants.
|
|
Packit |
f0b94e |
return Flags::SendA11yNotificationsIfShown;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
// We have asked accessibility service to remove the whole subtree
|
|
Packit |
f0b94e |
// of element which becomes invisible from the accessible tree, but
|
|
Packit |
f0b94e |
// this element is visible, so we need to add it back.
|
|
Packit |
f0b94e |
needsNotify = true;
|
|
Packit |
f0b94e |
} else {
|
|
Packit |
f0b94e |
// If we shouldn't skip in any case, we need to check whether our
|
|
Packit |
f0b94e |
// own visibility has changed.
|
|
Packit |
f0b94e |
bool wasVisible = aOldStyleContext->StyleVisibility()->IsVisible();
|
|
Packit |
f0b94e |
needsNotify = wasVisible != isVisible;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (needsNotify) {
|
|
Packit |
f0b94e |
nsIPresShell* presShell = aPresContext->PresShell();
|
|
Packit |
f0b94e |
if (isVisible) {
|
|
Packit |
f0b94e |
accService->ContentRangeInserted(presShell, aElement->GetParent(),
|
|
Packit |
f0b94e |
aElement, aElement->GetNextSibling());
|
|
Packit |
f0b94e |
// We are adding the subtree. Accessibility service would handle
|
|
Packit |
f0b94e |
// descendants, so we should just skip them from notifying.
|
|
Packit |
f0b94e |
return Flags::SkipA11yNotifications;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
// Remove the subtree of this invisible element, and ask any shown
|
|
Packit |
f0b94e |
// descendant to add themselves back.
|
|
Packit |
f0b94e |
accService->ContentRemoved(presShell, aElement);
|
|
Packit |
f0b94e |
return Flags::SendA11yNotificationsIfShown;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
#endif
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
return Flags::Empty;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
bool ServoRestyleManager::ProcessPostTraversal(
|
|
Packit |
f0b94e |
Element* aElement, ServoStyleContext* aParentContext,
|
|
Packit |
f0b94e |
ServoRestyleState& aRestyleState, ServoPostTraversalFlags aFlags) {
|
|
Packit |
f0b94e |
nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(aElement);
|
|
Packit |
f0b94e |
nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// NOTE(emilio): This is needed because for table frames the bit is set on the
|
|
Packit |
f0b94e |
// table wrapper (which is the primary frame), not on the table itself.
|
|
Packit |
f0b94e |
const bool isOutOfFlow =
|
|
Packit |
f0b94e |
primaryFrame && primaryFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Grab the change hint from Servo.
|
|
Packit |
f0b94e |
bool wasRestyled;
|
|
Packit |
f0b94e |
nsChangeHint changeHint =
|
|
Packit |
f0b94e |
static_cast<nsChangeHint>(Servo_TakeChangeHint(aElement, &wasRestyled));
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// We should really fix the weird primary frame mapping for image maps
|
|
Packit |
f0b94e |
// (bug 135040)...
|
|
Packit |
f0b94e |
if (styleFrame && styleFrame->GetContent() != aElement) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(static_cast<nsImageFrame*>(do_QueryFrame(styleFrame)));
|
|
Packit |
f0b94e |
styleFrame = nullptr;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Handle lazy frame construction by posting a reconstruct for any lazily-
|
|
Packit |
f0b94e |
// constructed roots.
|
|
Packit |
f0b94e |
if (aElement->HasFlag(NODE_NEEDS_FRAME)) {
|
|
Packit |
f0b94e |
changeHint |= nsChangeHint_ReconstructFrame;
|
|
Packit |
f0b94e |
MOZ_ASSERT(!styleFrame);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (styleFrame) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(primaryFrame);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsIFrame* maybeAnonBoxChild;
|
|
Packit |
f0b94e |
if (isOutOfFlow) {
|
|
Packit |
f0b94e |
maybeAnonBoxChild = primaryFrame->GetPlaceholderFrame();
|
|
Packit |
f0b94e |
} else {
|
|
Packit |
f0b94e |
maybeAnonBoxChild = primaryFrame;
|
|
Packit |
f0b94e |
changeHint = NS_RemoveSubsumedHints(
|
|
Packit |
f0b94e |
changeHint, aRestyleState.ChangesHandledFor(*styleFrame));
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// If the parent wasn't restyled, the styles of our anon box parents won't
|
|
Packit |
f0b94e |
// change either.
|
|
Packit |
f0b94e |
if ((aFlags & ServoPostTraversalFlags::ParentWasRestyled) &&
|
|
Packit |
f0b94e |
maybeAnonBoxChild->ParentIsWrapperAnonBox()) {
|
|
Packit |
f0b94e |
aRestyleState.AddPendingWrapperRestyle(
|
|
Packit |
f0b94e |
ServoRestyleState::TableAwareParentFor(maybeAnonBoxChild));
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Although we shouldn't generate non-ReconstructFrame hints for elements with
|
|
Packit |
f0b94e |
// no frames, we can still get them here if they were explicitly posted by
|
|
Packit |
f0b94e |
// PostRestyleEvent, such as a RepaintFrame hint when a :link changes to be
|
|
Packit |
f0b94e |
// :visited. Skip processing these hints if there is no frame.
|
|
Packit |
f0b94e |
if ((styleFrame || (changeHint & nsChangeHint_ReconstructFrame)) &&
|
|
Packit |
f0b94e |
changeHint) {
|
|
Packit |
f0b94e |
aRestyleState.ChangeList().AppendChange(styleFrame, aElement, changeHint);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// If our change hint is reconstruct, we delegate to the frame constructor,
|
|
Packit |
f0b94e |
// which consumes the new style and expects the old style to be on the frame.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// XXXbholley: We should teach the frame constructor how to clear the dirty
|
|
Packit |
f0b94e |
// descendants bit to avoid the traversal here.
|
|
Packit |
f0b94e |
if (changeHint & nsChangeHint_ReconstructFrame) {
|
|
Packit |
f0b94e |
ClearRestyleStateFromSubtree(aElement);
|
|
Packit |
f0b94e |
return true;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// TODO(emilio): We could avoid some refcount traffic here, specially in the
|
|
Packit |
f0b94e |
// ServoStyleContext case, which uses atomic refcounting.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// Hold the old style context alive, because it could become a dangling
|
|
Packit |
f0b94e |
// pointer during the replacement. In practice it's not a huge deal, but
|
|
Packit |
f0b94e |
// better not playing with dangling pointers if not needed.
|
|
Packit |
f0b94e |
RefPtr<ServoStyleContext> oldStyleContext =
|
|
Packit |
f0b94e |
styleFrame ? styleFrame->StyleContext()->AsServo() : nullptr;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsStyleContext* displayContentsStyle = nullptr;
|
|
Packit |
f0b94e |
// FIXME(emilio, bug 1303605): This can be simpler for Servo.
|
|
Packit |
f0b94e |
// Note that we intentionally don't check for display: none content.
|
|
Packit |
f0b94e |
if (!oldStyleContext) {
|
|
Packit |
f0b94e |
displayContentsStyle =
|
|
Packit |
f0b94e |
PresContext()->FrameConstructor()->GetDisplayContentsStyleFor(aElement);
|
|
Packit |
f0b94e |
if (displayContentsStyle) {
|
|
Packit |
f0b94e |
oldStyleContext = displayContentsStyle->AsServo();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Maybe<ServoRestyleState> thisFrameRestyleState;
|
|
Packit |
f0b94e |
if (styleFrame) {
|
|
Packit |
f0b94e |
auto type = isOutOfFlow ? ServoRestyleState::Type::OutOfFlow
|
|
Packit |
f0b94e |
: ServoRestyleState::Type::InFlow;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
thisFrameRestyleState.emplace(*styleFrame, aRestyleState, changeHint, type);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// We can't really assume as used changes from display: contents elements (or
|
|
Packit |
f0b94e |
// other elements without frames).
|
|
Packit |
f0b94e |
ServoRestyleState& childrenRestyleState =
|
|
Packit |
f0b94e |
thisFrameRestyleState ? *thisFrameRestyleState : aRestyleState;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
RefPtr<ServoStyleContext> upToDateContext =
|
|
Packit |
f0b94e |
wasRestyled ? aRestyleState.StyleSet().ResolveServoStyle(aElement)
|
|
Packit |
f0b94e |
: oldStyleContext;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ServoPostTraversalFlags childrenFlags =
|
|
Packit |
f0b94e |
wasRestyled ? ServoPostTraversalFlags::ParentWasRestyled
|
|
Packit |
f0b94e |
: ServoPostTraversalFlags::Empty;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (wasRestyled && oldStyleContext) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(styleFrame || displayContentsStyle);
|
|
Packit |
f0b94e |
MOZ_ASSERT(oldStyleContext->ComputedData() !=
|
|
Packit |
f0b94e |
upToDateContext->ComputedData());
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
upToDateContext->ResolveSameStructsAs(oldStyleContext);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// We want to walk all the continuations here, even the ones with different
|
|
Packit |
f0b94e |
// styles. In practice, the only reason we get continuations with different
|
|
Packit |
f0b94e |
// styles here is ::first-line (::first-letter never affects element
|
|
Packit |
f0b94e |
// styles). But in that case, newContext is the right context for the
|
|
Packit |
f0b94e |
// _later_ continuations anyway (the ones not affected by ::first-line), not
|
|
Packit |
f0b94e |
// the earlier ones, so there is no point stopping right at the point when
|
|
Packit |
f0b94e |
// we'd actually be setting the right style context.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// This does mean that we may be setting the wrong style context on our
|
|
Packit |
f0b94e |
// initial continuations; ::first-line fixes that up after the fact.
|
|
Packit |
f0b94e |
for (nsIFrame* f = styleFrame; f; f = f->GetNextContinuation()) {
|
|
Packit |
f0b94e |
MOZ_ASSERT_IF(f != styleFrame, !f->GetAdditionalStyleContext(0));
|
|
Packit |
f0b94e |
f->SetStyleContext(upToDateContext);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (MOZ_UNLIKELY(displayContentsStyle)) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(!styleFrame);
|
|
Packit |
f0b94e |
PresContext()
|
|
Packit |
f0b94e |
->FrameConstructor()
|
|
Packit |
f0b94e |
->ChangeRegisteredDisplayContentsStyleFor(aElement, upToDateContext);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (styleFrame) {
|
|
Packit |
f0b94e |
UpdateAdditionalStyleContexts(styleFrame, aRestyleState);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (!aElement->GetParent()) {
|
|
Packit |
f0b94e |
// This is the root. Update styles on the viewport as needed.
|
|
Packit |
f0b94e |
ViewportFrame* viewport =
|
|
Packit |
f0b94e |
do_QueryFrame(mPresContext->PresShell()->GetRootFrame());
|
|
Packit |
f0b94e |
if (viewport) {
|
|
Packit |
f0b94e |
// NB: The root restyle state, not the one for our children!
|
|
Packit |
f0b94e |
viewport->UpdateStyle(aRestyleState);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Some changes to animations don't affect the computed style and yet still
|
|
Packit |
f0b94e |
// require the layer to be updated. For example, pausing an animation via
|
|
Packit |
f0b94e |
// the Web Animations API won't affect an element's style but still
|
|
Packit |
f0b94e |
// requires to update the animation on the layer.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// We can sometimes reach this when the animated style is being removed.
|
|
Packit |
f0b94e |
// Since AddLayerChangesForAnimation checks if |styleFrame| has a transform
|
|
Packit |
f0b94e |
// style or not, we need to call it *after* setting |newContext| to
|
|
Packit |
f0b94e |
// |styleFrame| to ensure the animated transform has been removed first.
|
|
Packit |
f0b94e |
AddLayerChangesForAnimation(styleFrame, aElement,
|
|
Packit |
f0b94e |
aRestyleState.ChangeList());
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
childrenFlags |= SendA11yNotifications(
|
|
Packit |
f0b94e |
mPresContext, aElement, oldStyleContext, upToDateContext, aFlags);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
const bool traverseElementChildren =
|
|
Packit |
f0b94e |
aElement->HasAnyOfFlags(Element::kAllServoDescendantBits);
|
|
Packit |
f0b94e |
const bool traverseTextChildren =
|
|
Packit |
f0b94e |
wasRestyled || aElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES);
|
|
Packit |
f0b94e |
bool recreatedAnyContext = wasRestyled;
|
|
Packit |
f0b94e |
if (traverseElementChildren || traverseTextChildren) {
|
|
Packit |
f0b94e |
StyleChildrenIterator it(aElement);
|
|
Packit |
f0b94e |
TextPostTraversalState textState(*aElement, upToDateContext,
|
|
Packit |
f0b94e |
displayContentsStyle && wasRestyled,
|
|
Packit |
f0b94e |
childrenRestyleState);
|
|
Packit |
f0b94e |
for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
|
|
Packit |
f0b94e |
if (traverseElementChildren && n->IsElement()) {
|
|
Packit |
f0b94e |
recreatedAnyContext |=
|
|
Packit |
f0b94e |
ProcessPostTraversal(n->AsElement(), upToDateContext,
|
|
Packit |
f0b94e |
childrenRestyleState, childrenFlags);
|
|
Packit |
f0b94e |
} else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) {
|
|
Packit |
f0b94e |
recreatedAnyContext |= ProcessPostTraversalForText(
|
|
Packit |
f0b94e |
n, textState, childrenRestyleState, childrenFlags);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// We want to update frame pseudo-element styles after we've traversed our
|
|
Packit |
f0b94e |
// kids, because some of those updates (::first-line/::first-letter) need to
|
|
Packit |
f0b94e |
// modify the styles of the kids, and the child traversal above would just
|
|
Packit |
f0b94e |
// clobber those modifications.
|
|
Packit |
f0b94e |
if (styleFrame) {
|
|
Packit |
f0b94e |
if (wasRestyled) {
|
|
Packit |
f0b94e |
// Make sure to update anon boxes and pseudo bits after updating text,
|
|
Packit |
f0b94e |
// otherwise ProcessPostTraversalForText could clobber first-letter
|
|
Packit |
f0b94e |
// styles, for example.
|
|
Packit |
f0b94e |
styleFrame->UpdateStyleOfOwnedAnonBoxes(childrenRestyleState);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
// Process anon box wrapper frames before ::first-line bits, but _after_
|
|
Packit |
f0b94e |
// owned anon boxes, since the children wrapper anon boxes could be
|
|
Packit |
f0b94e |
// inheriting from our own owned anon boxes.
|
|
Packit |
f0b94e |
childrenRestyleState.ProcessWrapperRestyles(styleFrame);
|
|
Packit |
f0b94e |
if (wasRestyled) {
|
|
Packit |
f0b94e |
UpdateFramePseudoElementStyles(styleFrame, childrenRestyleState);
|
|
Packit |
f0b94e |
} else if (traverseElementChildren &&
|
|
Packit |
f0b94e |
styleFrame->IsFrameOfType(nsIFrame::eBlockFrame)) {
|
|
Packit |
f0b94e |
// Even if we were not restyled, if we're a block with a first-line and
|
|
Packit |
f0b94e |
// one of our descendant elements which is on the first line was restyled,
|
|
Packit |
f0b94e |
// we need to update the styles of things on the first line, because
|
|
Packit |
f0b94e |
// they're wrong now.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// FIXME(bz) Could we do better here? For example, could we keep track of
|
|
Packit |
f0b94e |
// frames that are "block with a ::first-line so we could avoid
|
|
Packit |
f0b94e |
// IsFrameOfType() and digging about for the first-line frame if not?
|
|
Packit |
f0b94e |
// Could we keep track of whether the element children we actually restyle
|
|
Packit |
f0b94e |
// are affected by first-line? Something else? Bug 1385443 tracks making
|
|
Packit |
f0b94e |
// this better.
|
|
Packit |
f0b94e |
nsIFrame* firstLineFrame =
|
|
Packit |
f0b94e |
static_cast<nsBlockFrame*>(styleFrame)->GetFirstLineFrame();
|
|
Packit |
f0b94e |
if (firstLineFrame) {
|
|
Packit |
f0b94e |
for (nsIFrame* kid : firstLineFrame->PrincipalChildList()) {
|
|
Packit |
f0b94e |
ReparentStyleContext(kid);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
aElement->UnsetFlags(Element::kAllServoDescendantBits);
|
|
Packit |
f0b94e |
return recreatedAnyContext;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
bool ServoRestyleManager::ProcessPostTraversalForText(
|
|
Packit |
f0b94e |
nsIContent* aTextNode, TextPostTraversalState& aPostTraversalState,
|
|
Packit |
f0b94e |
ServoRestyleState& aRestyleState, ServoPostTraversalFlags aFlags) {
|
|
Packit |
f0b94e |
// Handle lazy frame construction.
|
|
Packit |
f0b94e |
if (aTextNode->HasFlag(NODE_NEEDS_FRAME)) {
|
|
Packit |
f0b94e |
aPostTraversalState.ChangeList().AppendChange(
|
|
Packit |
f0b94e |
nullptr, aTextNode, nsChangeHint_ReconstructFrame);
|
|
Packit |
f0b94e |
return true;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Handle restyle.
|
|
Packit |
f0b94e |
nsIFrame* primaryFrame = aTextNode->GetPrimaryFrame();
|
|
Packit |
f0b94e |
if (!primaryFrame) {
|
|
Packit |
f0b94e |
return false;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// If the parent wasn't restyled, the styles of our anon box parents won't
|
|
Packit |
f0b94e |
// change either.
|
|
Packit |
f0b94e |
if ((aFlags & ServoPostTraversalFlags::ParentWasRestyled) &&
|
|
Packit |
f0b94e |
primaryFrame->ParentIsWrapperAnonBox()) {
|
|
Packit |
f0b94e |
aRestyleState.AddPendingWrapperRestyle(
|
|
Packit |
f0b94e |
ServoRestyleState::TableAwareParentFor(primaryFrame));
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsStyleContext& newContext = aPostTraversalState.ComputeStyle(aTextNode);
|
|
Packit |
f0b94e |
aPostTraversalState.ComputeHintIfNeeded(aTextNode, primaryFrame, newContext);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// We want to walk all the continuations here, even the ones with different
|
|
Packit |
f0b94e |
// styles. In practice, the only reasons we get continuations with different
|
|
Packit |
f0b94e |
// styles are ::first-line and ::first-letter. But in those cases,
|
|
Packit |
f0b94e |
// newContext is the right context for the _later_ continuations anyway (the
|
|
Packit |
f0b94e |
// ones not affected by ::first-line/::first-letter), not the earlier ones,
|
|
Packit |
f0b94e |
// so there is no point stopping right at the point when we'd actually be
|
|
Packit |
f0b94e |
// setting the right style context.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// This does mean that we may be setting the wrong style context on our
|
|
Packit |
f0b94e |
// initial continuations; ::first-line/::first-letter fix that up after the
|
|
Packit |
f0b94e |
// fact.
|
|
Packit |
f0b94e |
for (nsIFrame* f = primaryFrame; f; f = f->GetNextContinuation()) {
|
|
Packit |
f0b94e |
f->SetStyleContext(&newContext);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
return true;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleManager::ClearSnapshots() {
|
|
Packit |
f0b94e |
for (auto iter = mSnapshots.Iter(); !iter.Done(); iter.Next()) {
|
|
Packit |
f0b94e |
iter.Key()->UnsetFlags(ELEMENT_HAS_SNAPSHOT | ELEMENT_HANDLED_SNAPSHOT);
|
|
Packit |
f0b94e |
iter.Remove();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ServoElementSnapshot& ServoRestyleManager::SnapshotFor(Element* aElement) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(!mInStyleRefresh);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// NOTE(emilio): We can handle snapshots from a one-off restyle of those that
|
|
Packit |
f0b94e |
// we do to restyle stuff for reconstruction, for example.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// It seems to be the case that we always flush in between that happens and
|
|
Packit |
f0b94e |
// the next attribute change, so we can assert that we haven't handled the
|
|
Packit |
f0b94e |
// snapshot here yet. If this assertion didn't hold, we'd need to unset that
|
|
Packit |
f0b94e |
// flag from here too.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// Can't wait to make ProcessPendingRestyles the only entry-point for styling,
|
|
Packit |
f0b94e |
// so this becomes much easier to reason about. Today is not that day though.
|
|
Packit |
f0b94e |
MOZ_ASSERT(aElement->HasServoData());
|
|
Packit |
f0b94e |
MOZ_ASSERT(!aElement->HasFlag(ELEMENT_HANDLED_SNAPSHOT));
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ServoElementSnapshot* snapshot = mSnapshots.LookupOrAdd(aElement, aElement);
|
|
Packit |
f0b94e |
aElement->SetFlags(ELEMENT_HAS_SNAPSHOT);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Now that we have a snapshot, make sure a restyle is triggered.
|
|
Packit |
f0b94e |
aElement->NoteDirtyForServo();
|
|
Packit |
f0b94e |
return *snapshot;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleManager::DoProcessPendingRestyles(ServoTraversalFlags aFlags) {
|
|
Packit |
f0b94e |
nsPresContext* presContext = PresContext();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
MOZ_ASSERT(presContext->Document(), "No document? Pshaw!");
|
|
Packit |
f0b94e |
// FIXME(emilio): In the "flush animations" case, ideally, we should only
|
|
Packit |
f0b94e |
// recascade animation styles running on the compositor, so we shouldn't care
|
|
Packit |
f0b94e |
// about other styles, or new rules that apply to the page...
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// However, that's not true as of right now, see bug 1388031 and bug 1388692.
|
|
Packit |
f0b94e |
MOZ_ASSERT((aFlags & ServoTraversalFlags::FlushThrottledAnimations) ||
|
|
Packit |
f0b94e |
!presContext->HasPendingMediaQueryUpdates(),
|
|
Packit |
f0b94e |
"Someone forgot to update media queries?");
|
|
Packit |
f0b94e |
MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), "Missing a script blocker!");
|
|
Packit |
f0b94e |
MOZ_ASSERT(!mInStyleRefresh, "Reentrant call?");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (MOZ_UNLIKELY(!presContext->PresShell()->DidInitialize())) {
|
|
Packit |
f0b94e |
// PresShell::FlushPendingNotifications doesn't early-return in the case
|
|
Packit |
f0b94e |
// where the PresShell hasn't yet been initialized (and therefore we haven't
|
|
Packit |
f0b94e |
// yet done the initial style traversal of the DOM tree). We should arguably
|
|
Packit |
f0b94e |
// fix up the callers and assert against this case, but we just detect and
|
|
Packit |
f0b94e |
// handle it for now.
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Create a AnimationsWithDestroyedFrame during restyling process to
|
|
Packit |
f0b94e |
// stop animations and transitions on elements that have no frame at the end
|
|
Packit |
f0b94e |
// of the restyling process.
|
|
Packit |
f0b94e |
AnimationsWithDestroyedFrame animationsWithDestroyedFrame(this);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ServoStyleSet* styleSet = StyleSet();
|
|
Packit |
f0b94e |
nsIDocument* doc = presContext->Document();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Ensure the refresh driver is active during traversal to avoid mutating
|
|
Packit |
f0b94e |
// mActiveTimer and mMostRecentRefresh time.
|
|
Packit |
f0b94e |
presContext->RefreshDriver()->MostRecentRefresh();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Perform the Servo traversal, and the post-traversal if required. We do this
|
|
Packit |
f0b94e |
// in a loop because certain rare paths in the frame constructor (like
|
|
Packit |
f0b94e |
// uninstalling XBL bindings) can trigger additional style validations.
|
|
Packit |
f0b94e |
mInStyleRefresh = true;
|
|
Packit |
f0b94e |
if (mHaveNonAnimationRestyles) {
|
|
Packit |
f0b94e |
++mAnimationGeneration;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (mRestyleForCSSRuleChanges) {
|
|
Packit |
f0b94e |
aFlags |= ServoTraversalFlags::ForCSSRuleChanges;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
while (styleSet->StyleDocument(aFlags)) {
|
|
Packit |
f0b94e |
ClearSnapshots();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsStyleChangeList currentChanges(StyleBackendType::Servo);
|
|
Packit |
f0b94e |
bool anyStyleChanged = false;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Recreate style contexts, and queue up change hints (which also handle
|
|
Packit |
f0b94e |
// lazy frame construction).
|
|
Packit |
f0b94e |
{
|
|
Packit |
f0b94e |
AutoRestyleTimelineMarker marker(presContext->GetDocShell(), false);
|
|
Packit |
f0b94e |
DocumentStyleRootIterator iter(doc->GetServoRestyleRoot());
|
|
Packit |
f0b94e |
while (Element* root = iter.GetNextStyleRoot()) {
|
|
Packit |
f0b94e |
nsTArray<nsIFrame*> wrappersToRestyle;
|
|
Packit |
f0b94e |
ServoRestyleState state(*styleSet, currentChanges, wrappersToRestyle);
|
|
Packit |
f0b94e |
ServoPostTraversalFlags flags = ServoPostTraversalFlags::Empty;
|
|
Packit |
f0b94e |
anyStyleChanged |= ProcessPostTraversal(root, nullptr, state, flags);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
doc->ClearServoRestyleRoot();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Process the change hints.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// Unfortunately, the frame constructor can generate new change hints while
|
|
Packit |
f0b94e |
// processing existing ones. We redirect those into a secondary queue and
|
|
Packit |
f0b94e |
// iterate until there's nothing left.
|
|
Packit |
f0b94e |
{
|
|
Packit |
f0b94e |
AutoTimelineMarker marker(presContext->GetDocShell(),
|
|
Packit |
f0b94e |
"StylesApplyChanges");
|
|
Packit |
f0b94e |
ReentrantChangeList newChanges;
|
|
Packit |
f0b94e |
mReentrantChanges = &newChanges;
|
|
Packit |
f0b94e |
while (!currentChanges.IsEmpty()) {
|
|
Packit |
f0b94e |
ProcessRestyledFrames(currentChanges);
|
|
Packit |
f0b94e |
MOZ_ASSERT(currentChanges.IsEmpty());
|
|
Packit |
f0b94e |
for (ReentrantChange& change : newChanges) {
|
|
Packit |
f0b94e |
if (!(change.mHint & nsChangeHint_ReconstructFrame) &&
|
|
Packit |
f0b94e |
!change.mContent->GetPrimaryFrame()) {
|
|
Packit |
f0b94e |
// SVG Elements post change hints without ensuring that the primary
|
|
Packit |
f0b94e |
// frame will be there after that (see bug 1366142).
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// Just ignore those, since we can't really process them.
|
|
Packit |
f0b94e |
continue;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
currentChanges.AppendChange(change.mContent->GetPrimaryFrame(),
|
|
Packit |
f0b94e |
change.mContent, change.mHint);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
newChanges.Clear();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
mReentrantChanges = nullptr;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (anyStyleChanged) {
|
|
Packit |
f0b94e |
// Maybe no styles changed when:
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// * Only explicit change hints were posted in the first place.
|
|
Packit |
f0b94e |
// * When an attribute or state change in the content happens not to need
|
|
Packit |
f0b94e |
// a restyle after all.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// In any case, we don't need to increment the restyle generation in that
|
|
Packit |
f0b94e |
// case.
|
|
Packit |
f0b94e |
IncrementRestyleGeneration();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
doc->ClearServoRestyleRoot();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
FlushOverflowChangedTracker();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ClearSnapshots();
|
|
Packit |
f0b94e |
styleSet->AssertTreeIsClean();
|
|
Packit |
f0b94e |
mHaveNonAnimationRestyles = false;
|
|
Packit |
f0b94e |
mRestyleForCSSRuleChanges = false;
|
|
Packit |
f0b94e |
mInStyleRefresh = false;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Now that everything has settled, see if we have enough free rule nodes in
|
|
Packit |
f0b94e |
// the tree to warrant sweeping them.
|
|
Packit |
f0b94e |
styleSet->MaybeGCRuleTree();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Note: We are in the scope of |animationsWithDestroyedFrame|, so
|
|
Packit |
f0b94e |
// |mAnimationsWithDestroyedFrame| is still valid.
|
|
Packit |
f0b94e |
MOZ_ASSERT(mAnimationsWithDestroyedFrame);
|
|
Packit |
f0b94e |
mAnimationsWithDestroyedFrame->StopAnimationsForElementsWithoutFrames();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
#ifdef DEBUG
|
|
Packit |
f0b94e |
static void VerifyFlatTree(const nsIContent& aContent) {
|
|
Packit |
f0b94e |
StyleChildrenIterator iter(&aContent);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
for (auto* content = iter.GetNextChild(); content;
|
|
Packit |
f0b94e |
content = iter.GetNextChild()) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(content->GetFlattenedTreeParentNodeForStyle() == &aContent);
|
|
Packit |
f0b94e |
VerifyFlatTree(*content);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
#endif
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleManager::ProcessPendingRestyles() {
|
|
Packit |
f0b94e |
#ifdef DEBUG
|
|
Packit |
f0b94e |
if (auto* root = mPresContext->Document()->GetRootElement()) {
|
|
Packit |
f0b94e |
VerifyFlatTree(*root);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
#endif
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
DoProcessPendingRestyles(ServoTraversalFlags::Empty);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleManager::ProcessAllPendingAttributeAndStateInvalidations() {
|
|
Packit |
f0b94e |
if (mSnapshots.IsEmpty()) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
for (auto iter = mSnapshots.Iter(); !iter.Done(); iter.Next()) {
|
|
Packit |
f0b94e |
// Servo data for the element might have been dropped. (e.g. by removing
|
|
Packit |
f0b94e |
// from its document)
|
|
Packit |
f0b94e |
if (iter.Key()->HasFlag(ELEMENT_HAS_SNAPSHOT)) {
|
|
Packit |
f0b94e |
Servo_ProcessInvalidations(StyleSet()->RawSet(), iter.Key(), &mSnapshots);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
ClearSnapshots();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
bool ServoRestyleManager::HasPendingRestyleAncestor(Element* aElement) const {
|
|
Packit |
f0b94e |
return Servo_HasPendingRestyleAncestor(aElement);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleManager::UpdateOnlyAnimationStyles() {
|
|
Packit |
f0b94e |
bool doCSS = PresContext()->EffectCompositor()->HasPendingStyleUpdates();
|
|
Packit |
f0b94e |
if (!doCSS) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
DoProcessPendingRestyles(ServoTraversalFlags::FlushThrottledAnimations);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleManager::ContentStateChanged(nsIContent* aContent,
|
|
Packit |
f0b94e |
EventStates aChangedBits) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(!mInStyleRefresh);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (!aContent->IsElement()) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Element* aElement = aContent->AsElement();
|
|
Packit |
f0b94e |
if (!aElement->HasServoData()) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
const EventStates kVisitedAndUnvisited =
|
|
Packit |
f0b94e |
NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED;
|
|
Packit |
f0b94e |
if (aChangedBits.HasAllStates(kVisitedAndUnvisited) &&
|
|
Packit |
f0b94e |
!Gecko_VisitedStylesEnabled(aElement->OwnerDoc())) {
|
|
Packit |
f0b94e |
aChangedBits &= ~kVisitedAndUnvisited;
|
|
Packit |
f0b94e |
if (aChangedBits.IsEmpty()) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsChangeHint changeHint;
|
|
Packit |
f0b94e |
ContentStateChangedInternal(aElement, aChangedBits, &changeHint);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Don't bother taking a snapshot if no rules depend on these state bits.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// We always take a snapshot for the LTR/RTL event states, since Servo doesn't
|
|
Packit |
f0b94e |
// track those bits in the same way, and we know that :dir() rules are always
|
|
Packit |
f0b94e |
// present in UA style sheets.
|
|
Packit |
f0b94e |
if (!aChangedBits.HasAtLeastOneOfStates(DIRECTION_STATES) &&
|
|
Packit |
f0b94e |
!StyleSet()->HasStateDependency(*aElement, aChangedBits)) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ServoElementSnapshot& snapshot = SnapshotFor(aElement);
|
|
Packit |
f0b94e |
EventStates previousState = aElement->StyleState() ^ aChangedBits;
|
|
Packit |
f0b94e |
snapshot.AddState(previousState);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (changeHint) {
|
|
Packit |
f0b94e |
Servo_NoteExplicitHints(aElement, nsRestyleHint(0), changeHint);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Assuming we need to invalidate cached style in getComputedStyle for
|
|
Packit |
f0b94e |
// undisplayed elements, since we don't know if it is needed.
|
|
Packit |
f0b94e |
IncrementUndisplayedRestyleGeneration();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
static inline bool AttributeInfluencesOtherPseudoClassState(
|
|
Packit |
f0b94e |
const Element& aElement, const nsAtom* aAttribute) {
|
|
Packit |
f0b94e |
// We must record some state for :-moz-browser-frame and
|
|
Packit |
f0b94e |
// :-moz-table-border-nonzero.
|
|
Packit |
f0b94e |
if (aAttribute == nsGkAtoms::mozbrowser) {
|
|
Packit |
f0b94e |
return aElement.IsAnyOfHTMLElements(nsGkAtoms::iframe, nsGkAtoms::frame);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (aAttribute == nsGkAtoms::border) {
|
|
Packit |
f0b94e |
return aElement.IsHTMLElement(nsGkAtoms::table);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
return false;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
static inline bool NeedToRecordAttrChange(
|
|
Packit |
f0b94e |
const ServoStyleSet& aStyleSet, const Element& aElement,
|
|
Packit |
f0b94e |
int32_t aNameSpaceID, nsAtom* aAttribute,
|
|
Packit |
f0b94e |
bool* aInfluencesOtherPseudoClassState) {
|
|
Packit |
f0b94e |
*aInfluencesOtherPseudoClassState =
|
|
Packit |
f0b94e |
AttributeInfluencesOtherPseudoClassState(aElement, aAttribute);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// If the attribute influences one of the pseudo-classes that are backed by
|
|
Packit |
f0b94e |
// attributes, we just record it.
|
|
Packit |
f0b94e |
if (*aInfluencesOtherPseudoClassState) {
|
|
Packit |
f0b94e |
return true;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// We assume that id and class attributes are used in class/id selectors, and
|
|
Packit |
f0b94e |
// thus record them.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// TODO(emilio): We keep a filter of the ids in use somewhere in the StyleSet,
|
|
Packit |
f0b94e |
// presumably we could try to filter the old and new id, but it's not clear
|
|
Packit |
f0b94e |
// it's worth it.
|
|
Packit |
f0b94e |
if (aNameSpaceID == kNameSpaceID_None &&
|
|
Packit |
f0b94e |
(aAttribute == nsGkAtoms::id || aAttribute == nsGkAtoms::_class)) {
|
|
Packit |
f0b94e |
return true;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// We always record lang="", even though we force a subtree restyle when it
|
|
Packit |
f0b94e |
// changes, since it can change how its siblings match :lang(..) due to
|
|
Packit |
f0b94e |
// selectors like :lang(..) + div.
|
|
Packit |
f0b94e |
if (aAttribute == nsGkAtoms::lang) {
|
|
Packit |
f0b94e |
return true;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Otherwise, just record the attribute change if a selector in the page may
|
|
Packit |
f0b94e |
// reference it from an attribute selector.
|
|
Packit |
f0b94e |
return aStyleSet.MightHaveAttributeDependency(aElement, aAttribute);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleManager::AttributeWillChange(Element* aElement,
|
|
Packit |
f0b94e |
int32_t aNameSpaceID,
|
|
Packit |
f0b94e |
nsAtom* aAttribute,
|
|
Packit |
f0b94e |
int32_t aModType,
|
|
Packit |
f0b94e |
const nsAttrValue* aNewValue) {
|
|
Packit |
f0b94e |
TakeSnapshotForAttributeChange(aElement, aNameSpaceID, aAttribute);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleManager::ClassAttributeWillBeChangedBySMIL(Element* aElement) {
|
|
Packit |
f0b94e |
TakeSnapshotForAttributeChange(aElement, kNameSpaceID_None,
|
|
Packit |
f0b94e |
nsGkAtoms::_class);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleManager::TakeSnapshotForAttributeChange(Element* aElement,
|
|
Packit |
f0b94e |
int32_t aNameSpaceID,
|
|
Packit |
f0b94e |
nsAtom* aAttribute) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(!mInStyleRefresh);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (!aElement->HasServoData()) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
bool influencesOtherPseudoClassState;
|
|
Packit |
f0b94e |
if (!NeedToRecordAttrChange(*StyleSet(), *aElement, aNameSpaceID, aAttribute,
|
|
Packit |
f0b94e |
&influencesOtherPseudoClassState)) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// We cannot tell if the attribute change will affect the styles of
|
|
Packit |
f0b94e |
// undisplayed elements, because we don't actually restyle those elements
|
|
Packit |
f0b94e |
// during the restyle traversal. So just assume that the attribute change can
|
|
Packit |
f0b94e |
// cause the style to change.
|
|
Packit |
f0b94e |
IncrementUndisplayedRestyleGeneration();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Some other random attribute changes may also affect the transitions,
|
|
Packit |
f0b94e |
// so we also set this true here.
|
|
Packit |
f0b94e |
mHaveNonAnimationRestyles = true;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ServoElementSnapshot& snapshot = SnapshotFor(aElement);
|
|
Packit |
f0b94e |
snapshot.AddAttrs(aElement, aNameSpaceID, aAttribute);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (influencesOtherPseudoClassState) {
|
|
Packit |
f0b94e |
snapshot.AddOtherPseudoClassState(aElement);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// For some attribute changes we must restyle the whole subtree:
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// * is affected by the cellpadding on its ancestor table
|
|
Packit |
f0b94e |
// * lwtheme and lwthemetextcolor on root element of XUL document
|
|
Packit |
f0b94e |
// affects all descendants due to :-moz-lwtheme* pseudo-classes
|
|
Packit |
f0b94e |
// * lang="" and xml:lang="" can affect all descendants due to :lang()
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
static inline bool AttributeChangeRequiresSubtreeRestyle(
|
|
Packit |
f0b94e |
const Element& aElement, nsAtom* aAttr) {
|
|
Packit |
f0b94e |
if (aAttr == nsGkAtoms::cellpadding) {
|
|
Packit |
f0b94e |
return aElement.IsHTMLElement(nsGkAtoms::table);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
if (aAttr == nsGkAtoms::lwtheme || aAttr == nsGkAtoms::lwthemetextcolor) {
|
|
Packit |
f0b94e |
return aElement.GetNameSpaceID() == kNameSpaceID_XUL &&
|
|
Packit |
f0b94e |
&aElement == aElement.OwnerDoc()->GetRootElement();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
return aAttr == nsGkAtoms::lang;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleManager::AttributeChanged(Element* aElement,
|
|
Packit |
f0b94e |
int32_t aNameSpaceID,
|
|
Packit |
f0b94e |
nsAtom* aAttribute, int32_t aModType,
|
|
Packit |
f0b94e |
const nsAttrValue* aOldValue) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(!mInStyleRefresh);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
auto changeHint = nsChangeHint(0);
|
|
Packit |
f0b94e |
auto restyleHint = nsRestyleHint(0);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
changeHint |= aElement->GetAttributeChangeHint(aAttribute, aModType);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (aAttribute == nsGkAtoms::style) {
|
|
Packit |
f0b94e |
restyleHint |= eRestyle_StyleAttribute;
|
|
Packit |
f0b94e |
} else if (AttributeChangeRequiresSubtreeRestyle(*aElement, aAttribute)) {
|
|
Packit |
f0b94e |
restyleHint |= eRestyle_Subtree;
|
|
Packit |
f0b94e |
} else if (aElement->IsAttributeMapped(aAttribute)) {
|
|
Packit |
f0b94e |
restyleHint |= eRestyle_Self;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (nsIFrame* primaryFrame = aElement->GetPrimaryFrame()) {
|
|
Packit |
f0b94e |
// See if we have appearance information for a theme.
|
|
Packit |
f0b94e |
const nsStyleDisplay* disp = primaryFrame->StyleDisplay();
|
|
Packit |
f0b94e |
if (disp->mAppearance) {
|
|
Packit |
f0b94e |
nsITheme* theme = PresContext()->GetTheme();
|
|
Packit |
f0b94e |
if (theme && theme->ThemeSupportsWidget(PresContext(), primaryFrame,
|
|
Packit |
f0b94e |
disp->mAppearance)) {
|
|
Packit |
f0b94e |
bool repaint = false;
|
|
Packit |
f0b94e |
theme->WidgetStateChanged(primaryFrame, disp->mAppearance, aAttribute,
|
|
Packit |
f0b94e |
&repaint, aOldValue);
|
|
Packit |
f0b94e |
if (repaint) {
|
|
Packit |
f0b94e |
changeHint |= nsChangeHint_RepaintFrame;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
primaryFrame->AttributeChanged(aNameSpaceID, aAttribute, aModType);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (restyleHint || changeHint) {
|
|
Packit |
f0b94e |
Servo_NoteExplicitHints(aElement, restyleHint, changeHint);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (restyleHint) {
|
|
Packit |
f0b94e |
// Assuming we need to invalidate cached style in getComputedStyle for
|
|
Packit |
f0b94e |
// undisplayed elements, since we don't know if it is needed.
|
|
Packit |
f0b94e |
IncrementUndisplayedRestyleGeneration();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// If we change attributes, we have to mark this to be true, so we will
|
|
Packit |
f0b94e |
// increase the animation generation for the new created transition if any.
|
|
Packit |
f0b94e |
mHaveNonAnimationRestyles = true;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsresult ServoRestyleManager::ReparentStyleContext(nsIFrame* aFrame) {
|
|
Packit |
f0b94e |
// This is only called when moving frames in or out of the first-line
|
|
Packit |
f0b94e |
// pseudo-element (or one of its descendants). We can't say much about
|
|
Packit |
f0b94e |
// aFrame's ancestors, unfortunately (e.g. during a dynamic insert into
|
|
Packit |
f0b94e |
// something inside an inline-block on the first line the ancestors could be
|
|
Packit |
f0b94e |
// totally arbitrary), but we will definitely find a line frame on the
|
|
Packit |
f0b94e |
// ancestor chain. Note that the lineframe may not actually be the one that
|
|
Packit |
f0b94e |
// corresponds to ::first-line; when we're moving _out_ of the ::first-line it
|
|
Packit |
f0b94e |
// will be one of the continuations instead.
|
|
Packit |
f0b94e |
#ifdef DEBUG
|
|
Packit |
f0b94e |
{
|
|
Packit |
f0b94e |
nsIFrame* f = aFrame->GetParent();
|
|
Packit |
f0b94e |
while (f && !f->IsLineFrame()) {
|
|
Packit |
f0b94e |
f = f->GetParent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
MOZ_ASSERT(f, "Must have found a first-line frame");
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
#endif
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
DoReparentStyleContext(aFrame, *StyleSet());
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
return NS_OK;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleManager::DoReparentStyleContext(nsIFrame* aFrame,
|
|
Packit |
f0b94e |
ServoStyleSet& aStyleSet) {
|
|
Packit |
f0b94e |
if (aFrame->IsBackdropFrame()) {
|
|
Packit |
f0b94e |
// Style context of backdrop frame has no parent style context, and
|
|
Packit |
f0b94e |
// thus we do not need to reparent it.
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (aFrame->IsPlaceholderFrame()) {
|
|
Packit |
f0b94e |
// Also reparent the out-of-flow and all its continuations. We're doing
|
|
Packit |
f0b94e |
// this to match Gecko for now, but it's not clear that this behavior is
|
|
Packit |
f0b94e |
// correct per spec. It's certainly pretty odd for out-of-flows whose
|
|
Packit |
f0b94e |
// containing block is not within the first line.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// Right now we're somewhat inconsistent in this testcase:
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// <style>
|
|
Packit |
f0b94e |
// div { color: orange; clear: left; }
|
|
Packit |
f0b94e |
// div::first-line { color: blue; }
|
|
Packit |
f0b94e |
// </style>
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// What color is this text?
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// What color is this text?
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// We make the first float orange and the second float blue. On the other
|
|
Packit |
f0b94e |
// hand, if the float were within an inline-block that was on the first
|
|
Packit |
f0b94e |
// line, arguably it _should_ inherit from the ::first-line...
|
|
Packit |
f0b94e |
nsIFrame* outOfFlow =
|
|
Packit |
f0b94e |
nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
|
|
Packit |
f0b94e |
MOZ_ASSERT(outOfFlow, "no out-of-flow frame");
|
|
Packit |
f0b94e |
for (; outOfFlow; outOfFlow = outOfFlow->GetNextContinuation()) {
|
|
Packit |
f0b94e |
DoReparentStyleContext(outOfFlow, aStyleSet);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsIFrame* providerFrame;
|
|
Packit |
f0b94e |
nsStyleContext* newParentContext =
|
|
Packit |
f0b94e |
aFrame->GetParentStyleContext(&providerFrame);
|
|
Packit |
f0b94e |
// If our provider is our child, we want to reparent it first, because we
|
|
Packit |
f0b94e |
// inherit style from it.
|
|
Packit |
f0b94e |
bool isChild = providerFrame && providerFrame->GetParent() == aFrame;
|
|
Packit |
f0b94e |
nsIFrame* providerChild = nullptr;
|
|
Packit |
f0b94e |
if (isChild) {
|
|
Packit |
f0b94e |
DoReparentStyleContext(providerFrame, aStyleSet);
|
|
Packit |
f0b94e |
// Get the style context again after ReparentStyleContext() which might have
|
|
Packit |
f0b94e |
// changed it.
|
|
Packit |
f0b94e |
newParentContext = providerFrame->StyleContext();
|
|
Packit |
f0b94e |
providerChild = providerFrame;
|
|
Packit |
f0b94e |
MOZ_ASSERT(!providerFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
|
|
Packit |
f0b94e |
"Out of flow provider?");
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (!newParentContext) {
|
|
Packit |
f0b94e |
// No need to do anything here for this frame, but we should still reparent
|
|
Packit |
f0b94e |
// its descendants, because those may have styles that inherit from the
|
|
Packit |
f0b94e |
// parent of this frame (e.g. non-anonymous columns in an anonymous
|
|
Packit |
f0b94e |
// colgroup).
|
|
Packit |
f0b94e |
MOZ_ASSERT(aFrame->StyleContext()->IsNonInheritingAnonBox(),
|
|
Packit |
f0b94e |
"Why did this frame not end up with a parent context?");
|
|
Packit |
f0b94e |
ReparentFrameDescendants(aFrame, providerChild, aStyleSet);
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
bool isElement = aFrame->GetContent()->IsElement();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// We probably don't want to initiate transitions from
|
|
Packit |
f0b94e |
// ReparentStyleContext, since we call it during frame
|
|
Packit |
f0b94e |
// construction rather than in response to dynamic changes.
|
|
Packit |
f0b94e |
// Also see the comment at the start of
|
|
Packit |
f0b94e |
// nsTransitionManager::ConsiderInitiatingTransition.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// We don't try to do the fancy copying from previous continuations that
|
|
Packit |
f0b94e |
// GeckoRestyleManager does here, because that relies on knowing the parents
|
|
Packit |
f0b94e |
// of style contexts, and we don't know those.
|
|
Packit |
f0b94e |
ServoStyleContext* oldContext = aFrame->StyleContext()->AsServo();
|
|
Packit |
f0b94e |
Element* ourElement =
|
|
Packit |
f0b94e |
oldContext->GetPseudoType() == CSSPseudoElementType::NotPseudo &&
|
|
Packit |
f0b94e |
isElement
|
|
Packit |
f0b94e |
? aFrame->GetContent()->AsElement()
|
|
Packit |
f0b94e |
: nullptr;
|
|
Packit |
f0b94e |
ServoStyleContext* newParent = newParentContext->AsServo();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ServoStyleContext* newParentIgnoringFirstLine;
|
|
Packit |
f0b94e |
if (newParent->GetPseudoType() == CSSPseudoElementType::firstLine) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(providerFrame && providerFrame->GetParent()->IsFrameOfType(
|
|
Packit |
f0b94e |
nsIFrame::eBlockFrame),
|
|
Packit |
f0b94e |
"How could we get a ::first-line parent style without having "
|
|
Packit |
f0b94e |
"a ::first-line provider frame?");
|
|
Packit |
f0b94e |
// If newParent is a ::first-line style, get the parent blockframe, and then
|
|
Packit |
f0b94e |
// correct it for our pseudo as needed (e.g. stepping out of anon boxes).
|
|
Packit |
f0b94e |
// Use the resulting style for the "parent style ignoring ::first-line".
|
|
Packit |
f0b94e |
nsIFrame* blockFrame = providerFrame->GetParent();
|
|
Packit |
f0b94e |
nsIFrame* correctedFrame =
|
|
Packit |
f0b94e |
nsFrame::CorrectStyleParentFrame(blockFrame, oldContext->GetPseudo());
|
|
Packit |
f0b94e |
newParentIgnoringFirstLine = correctedFrame->StyleContext()->AsServo();
|
|
Packit |
f0b94e |
} else {
|
|
Packit |
f0b94e |
newParentIgnoringFirstLine = newParent;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (!providerFrame) {
|
|
Packit |
f0b94e |
// No providerFrame means we inherited from a display:contents thing. Our
|
|
Packit |
f0b94e |
// layout parent style is the style of our nearest ancestor frame. But we
|
|
Packit |
f0b94e |
// have to be careful to do that with our placeholder, not with us, if we're
|
|
Packit |
f0b94e |
// out of flow.
|
|
Packit |
f0b94e |
if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
|
|
Packit |
f0b94e |
aFrame->GetPlaceholderFrame()->GetLayoutParentStyleForOutOfFlow(
|
|
Packit |
f0b94e |
&providerFrame);
|
|
Packit |
f0b94e |
} else {
|
|
Packit |
f0b94e |
providerFrame = nsFrame::CorrectStyleParentFrame(aFrame->GetParent(),
|
|
Packit |
f0b94e |
oldContext->GetPseudo());
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
ServoStyleContext* layoutParent = providerFrame->StyleContext()->AsServo();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
RefPtr<ServoStyleContext> newContext = aStyleSet.ReparentStyleContext(
|
|
Packit |
f0b94e |
oldContext, newParent, newParentIgnoringFirstLine, layoutParent,
|
|
Packit |
f0b94e |
ourElement);
|
|
Packit |
f0b94e |
aFrame->SetStyleContext(newContext);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// This logic somewhat mirrors the logic in
|
|
Packit |
f0b94e |
// ServoRestyleManager::ProcessPostTraversal.
|
|
Packit |
f0b94e |
if (isElement) {
|
|
Packit |
f0b94e |
// We can't use UpdateAdditionalStyleContexts as-is because it needs a
|
|
Packit |
f0b94e |
// ServoRestyleState and maintaining one of those during a _frametree_
|
|
Packit |
f0b94e |
// traversal is basically impossible.
|
|
Packit |
f0b94e |
uint32_t index = 0;
|
|
Packit |
f0b94e |
while (nsStyleContext* oldAdditionalContext =
|
|
Packit |
f0b94e |
aFrame->GetAdditionalStyleContext(index)) {
|
|
Packit |
f0b94e |
RefPtr<ServoStyleContext> newAdditionalContext =
|
|
Packit |
f0b94e |
aStyleSet.ReparentStyleContext(oldAdditionalContext->AsServo(),
|
|
Packit |
f0b94e |
newContext, newContext, newContext,
|
|
Packit |
f0b94e |
nullptr);
|
|
Packit |
f0b94e |
aFrame->SetAdditionalStyleContext(index, newAdditionalContext);
|
|
Packit |
f0b94e |
++index;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Generally, owned anon boxes are our descendants. The only exceptions are
|
|
Packit |
f0b94e |
// tables (for the table wrapper) and inline frames (for the block part of the
|
|
Packit |
f0b94e |
// block-in-inline split). We're going to update our descendants when looping
|
|
Packit |
f0b94e |
// over kids, and we don't want to update the block part of a block-in-inline
|
|
Packit |
f0b94e |
// split if the inline is on the first line but the block is not (and if the
|
|
Packit |
f0b94e |
// block is, it's the child of something else on the first line and will get
|
|
Packit |
f0b94e |
// updated as a child). And given how this method ends up getting called, if
|
|
Packit |
f0b94e |
// we reach here for a table frame, we are already in the middle of
|
|
Packit |
f0b94e |
// reparenting the table wrapper frame. So no need to
|
|
Packit |
f0b94e |
// UpdateStyleOfOwnedAnonBoxes() here.
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ReparentFrameDescendants(aFrame, providerChild, aStyleSet);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// We do not need to do the equivalent of UpdateFramePseudoElementStyles,
|
|
Packit |
f0b94e |
// because those are hadled by our descendant walk.
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ServoRestyleManager::ReparentFrameDescendants(nsIFrame* aFrame,
|
|
Packit |
f0b94e |
nsIFrame* aProviderChild,
|
|
Packit |
f0b94e |
ServoStyleSet& aStyleSet) {
|
|
Packit |
f0b94e |
nsIFrame::ChildListIterator lists(aFrame);
|
|
Packit |
f0b94e |
for (; !lists.IsDone(); lists.Next()) {
|
|
Packit |
f0b94e |
for (nsIFrame* child : lists.CurrentList()) {
|
|
Packit |
f0b94e |
// only do frames that are in flow
|
|
Packit |
f0b94e |
if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
|
|
Packit |
f0b94e |
child != aProviderChild) {
|
|
Packit |
f0b94e |
DoReparentStyleContext(child, aStyleSet);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
} // namespace mozilla
|