Bug 1290335: stylo: Allow processing change hints generated from Servo.
MozReview-Commit-ID: Alc0wcXvHcD
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -62,55 +62,88 @@ ServoRestyleManager::RebuildAllStyleData
void
ServoRestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
nsRestyleHint aRestyleHint)
{
MOZ_CRASH("stylo: ServoRestyleManager::PostRebuildAllStyleDataEvent not implemented");
}
-/* static */ void
+void
ServoRestyleManager::RecreateStyleContexts(nsIContent* aContent,
nsStyleContext* aParentContext,
- ServoStyleSet* aStyleSet)
+ ServoStyleSet* aStyleSet,
+ nsStyleChangeList& aChangeListToProcess)
{
nsIFrame* primaryFrame = aContent->GetPrimaryFrame();
-
- // TODO: AFAIK this can happen when we have, let's say, display: none. Here we
- // should trigger frame construction if the element is actually dirty (I
- // guess), but we'd better do that once we have all the restyle hints thing
- // figured out.
- if (!primaryFrame) {
- aContent->UnsetFlags(NODE_IS_DIRTY_FOR_SERVO | NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
+ if (!primaryFrame && !aContent->IsDirtyForServo()) {
+ NS_WARNING("Frame not found for non-dirty content");
return;
}
if (aContent->IsDirtyForServo()) {
+ nsChangeHint changeHint;
+ if (primaryFrame) {
+ changeHint = primaryFrame->StyleContext()->ConsumeStoredChangeHint();
+ } else {
+ // TODO: Use the frame constructor's UndisplayedNodeMap to store the old
+ // style contexts, and thus the change hints. That way we can in most
+ // cases avoid generating ReconstructFrame and push it to the list just to
+ // notice at frame construction that it doesn't need a frame.
+ changeHint = nsChangeHint_ReconstructFrame;
+ }
+
+ // NB: The change list only expects elements.
+ if (changeHint && aContent->IsElement()) {
+ aChangeListToProcess.AppendChange(primaryFrame, aContent, changeHint);
+ }
+
+ if (!primaryFrame) {
+ // The frame reconstruction step will ask for the descendant's style
+ // correctly.
+ return;
+ }
+
+ // Even if we don't have a change hint, we still need to swap style contexts
+ // so our new style is updated properly.
RefPtr<ServoComputedValues> computedValues =
dont_AddRef(Servo_GetComputedValues(aContent));
// TODO: Figure out what pseudos does this content have, and do the proper
// thing with them.
- RefPtr<nsStyleContext> context =
+ RefPtr<nsStyleContext> newContext =
aStyleSet->GetContext(computedValues.forget(),
aParentContext,
nullptr,
CSSPseudoElementType::NotPseudo);
- // TODO: Compare old and new styles to generate restyle change hints, and
- // process them.
- primaryFrame->SetStyleContext(context.get());
+ RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext();
+ MOZ_ASSERT(oldStyleContext);
+ // XXX This could not always work as expected there are kinds of content
+ // with the first split and the last sharing style, but others not. We
+ // should handle those properly.
+ for (nsIFrame* f = primaryFrame; f;
+ f = GetNextContinuationWithSameStyle(f, oldStyleContext)) {
+ f->SetStyleContext(newContext);
+ }
+
+ // TODO: There are other continuations we still haven't restyled, mostly
+ // pseudo-elements. We have to deal with those, and with anonymous boxes.
aContent->UnsetFlags(NODE_IS_DIRTY_FOR_SERVO);
}
if (aContent->HasDirtyDescendantsForServo()) {
+ MOZ_ASSERT(primaryFrame,
+ "Frame construction should be scheduled, and it takes the "
+ "correct style for the children");
FlattenedChildIterator it(aContent);
for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
- RecreateStyleContexts(n, primaryFrame->StyleContext(), aStyleSet);
+ RecreateStyleContexts(n, primaryFrame->StyleContext(),
+ aStyleSet, aChangeListToProcess);
}
aContent->UnsetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
}
}
static void
MarkParentsAsHavingDirtyDescendants(Element* aElement)
{
@@ -138,79 +171,96 @@ MarkChildrenAsDirtyForServo(nsIContent*
if (hadChildren) {
aContent->SetHasDirtyDescendantsForServo();
}
}
void
ServoRestyleManager::NoteRestyleHint(Element* aElement, nsRestyleHint aHint)
{
- if (aHint & eRestyle_Self) {
+ const nsRestyleHint HANDLED_RESTYLE_HINTS = eRestyle_Self |
+ eRestyle_Subtree |
+ eRestyle_LaterSiblings |
+ eRestyle_SomeDescendants;
+ // NB: For Servo, at least for now, restyling and running selector-matching
+ // against the subtree is necessary as part of restyling the element, so
+ // processing eRestyle_Self will perform at least as much work as
+ // eRestyle_Subtree.
+ if (aHint & (eRestyle_Self | eRestyle_Subtree)) {
aElement->SetIsDirtyForServo();
MarkParentsAsHavingDirtyDescendants(aElement);
- // NB: For Servo, at least for now, restyling and running selector-matching
- // against the subtree is necessary as part of restyling the element, so
- // processing eRestyle_Self will perform at least as much work as
- // eRestyle_Subtree.
- } else if (aHint & eRestyle_Subtree) {
+ // NB: Servo gives us a eRestyle_SomeDescendants when it expects us to run
+ // selector matching on all the descendants. There's a bug on Servo to align
+ // meanings here (#12710) to avoid this potential source of confusion.
+ } else if (aHint & eRestyle_SomeDescendants) {
MarkChildrenAsDirtyForServo(aElement);
MarkParentsAsHavingDirtyDescendants(aElement);
}
if (aHint & eRestyle_LaterSiblings) {
for (nsINode* cur = aElement->GetNextSibling(); cur;
cur = cur->GetNextSibling()) {
- if (cur->IsContent()) {
- cur->SetIsDirtyForServo();
- }
+ cur->SetIsDirtyForServo();
}
}
// TODO: Handle all other nsRestyleHint values.
- if (aHint & ~(eRestyle_Self | eRestyle_Subtree | eRestyle_LaterSiblings)) {
+ if (aHint & ~HANDLED_RESTYLE_HINTS) {
NS_WARNING(nsPrintfCString("stylo: Unhandled restyle hint %s",
- RestyleManagerBase::RestyleHintToString(aHint).get()).get());
+ RestyleManagerBase::RestyleHintToString(aHint).get()).get());
}
}
void
ServoRestyleManager::ProcessPendingRestyles()
{
+ MOZ_ASSERT(PresContext()->Document(), "No document? Pshaw!");
+ MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), "Missing a script blocker!");
if (!HasPendingRestyles()) {
return;
}
+
ServoStyleSet* styleSet = StyleSet();
-
if (!styleSet->StylingStarted()) {
// If something caused us to restyle, and we haven't started styling yet,
// do nothing. Everything is dirty, and we'll style it all later.
return;
}
nsIDocument* doc = PresContext()->Document();
-
Element* root = doc->GetRootElement();
if (root) {
+ // First do any queued-up frame creation. (see bugs 827239 and 997506).
+ //
+ // XXXEmilio I'm calling this to avoid random behavior changes, since we
+ // delay frame construction after styling we should re-check once our
+ // model is more stable whether we can skip this call.
+ PresContext()->FrameConstructor()->CreateNeededFrames();
+
for (auto iter = mModifiedElements.Iter(); !iter.Done(); iter.Next()) {
ServoElementSnapshot* snapshot = iter.UserData();
Element* element = iter.Key();
// TODO: avoid the ComputeRestyleHint call if we already have the highest
// explicit restyle hint?
nsRestyleHint hint = styleSet->ComputeRestyleHint(element, snapshot);
hint |= snapshot->ExplicitRestyleHint();
if (hint) {
NoteRestyleHint(element, hint);
}
}
if (root->IsDirtyForServo() || root->HasDirtyDescendantsForServo()) {
styleSet->RestyleSubtree(root);
- RecreateStyleContexts(root, nullptr, styleSet);
+
+ nsStyleChangeList changeList;
+
+ RecreateStyleContexts(root, nullptr, styleSet, changeList);
+ ProcessRestyledFrames(changeList);
}
}
mModifiedElements.Clear();
// NB: we restyle from the root element, but the document also gets the
// HAS_DIRTY_DESCENDANTS flag as part of the loop on PostRestyleEvent, and we
// use that to check we have pending restyles.
@@ -309,18 +359,20 @@ ServoRestyleManager::SnapshotForElement(
// NB: aElement is the argument for the construction of the snapshot in the
// not found case.
return mModifiedElements.LookupOrAdd(aElement, aElement);
}
nsresult
ServoRestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
{
- MOZ_CRASH("stylo: ServoRestyleManager::ProcessRestyledFrames not implemented "
- "for Servo-backed style system");
+ // XXX Hook the overflow tracker somewhere.
+ OverflowChangedTracker overflowChangedTracker;
+ return base_type::ProcessRestyledFrames(aChangeList, *PresContext(),
+ overflowChangedTracker);
}
void
ServoRestyleManager::FlushOverflowChangedTracker()
{
MOZ_CRASH("stylo: ServoRestyleManager::FlushOverflowChangedTracker "
"not implemented for Servo-backed style system");
}
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.h
@@ -31,16 +31,18 @@ namespace mozilla {
/**
* Restyle manager for a Servo-backed style system.
*/
class ServoRestyleManager : public RestyleManagerBase
{
friend class ServoStyleSet;
public:
+ typedef RestyleManagerBase base_type;
+
NS_INLINE_DECL_REFCOUNTING(ServoRestyleManager)
explicit ServoRestyleManager(nsPresContext* aPresContext);
void PostRestyleEvent(dom::Element* aElement,
nsRestyleHint aRestyleHint,
nsChangeHint aMinChangeHint);
void PostRestyleEventForLazyConstruction();
@@ -85,24 +87,21 @@ private:
* The element-to-element snapshot table to compute restyle hints.
*/
nsClassHashtable<nsRefPtrHashKey<Element>, ServoElementSnapshot>
mModifiedElements;
/**
* Traverses a tree of content that Servo has just restyled, recreating style
* contexts for their frames with the new style data.
- *
- * This is just static so ServoStyleSet can mark this class as friend, so we
- * can access to the GetContext method without making it available to everyone
- * else.
*/
- static void RecreateStyleContexts(nsIContent* aContent,
- nsStyleContext* aParentContext,
- ServoStyleSet* aStyleSet);
+ void RecreateStyleContexts(nsIContent* aContent,
+ nsStyleContext* aParentContext,
+ ServoStyleSet* aStyleSet,
+ nsStyleChangeList& aChangeList);
/**
* Marks the tree with the appropriate dirty flags for the given restyle hint.
*/
static void NoteRestyleHint(Element* aElement, nsRestyleHint aRestyleHint);
inline ServoStyleSet* StyleSet() const
{
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -50,16 +50,17 @@ class FlattenedChildIterator;
class nsCSSFrameConstructor : public nsFrameManager
{
public:
typedef mozilla::CSSPseudoElementType CSSPseudoElementType;
typedef mozilla::dom::Element Element;
friend class mozilla::RestyleManager;
friend class mozilla::RestyleManagerBase;
+ friend class mozilla::ServoRestyleManager;
nsCSSFrameConstructor(nsIDocument* aDocument, nsIPresShell* aPresShell);
~nsCSSFrameConstructor(void) {
NS_ASSERTION(mUpdateCount == 0, "Dying in the middle of our own update?");
}
// get the alternate text for a content node
static void GetAlternateTextFor(nsIContent* aContent,
--- a/layout/base/nsStyleChangeList.h
+++ b/layout/base/nsStyleChangeList.h
@@ -35,17 +35,17 @@ public:
nsStyleChangeList();
~nsStyleChangeList();
int32_t Count(void) const {
return mCount;
}
/**
- * Fills in pointers without reference counting.
+ * Fills in pointers without reference counting.
*/
nsresult ChangeAt(int32_t aIndex, nsIFrame*& aFrame, nsIContent*& aContent,
nsChangeHint& aHint) const;
/**
* Fills in a pointer to the list entry storage (no reference counting
* involved).
*/
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -179,37 +179,72 @@ Gecko_SetNodeFlags(RawGeckoNode* aNode,
}
void
Gecko_UnsetNodeFlags(RawGeckoNode* aNode, uint32_t aFlags)
{
aNode->UnsetFlags(aFlags);
}
+nsStyleContext*
+Gecko_GetStyleContext(RawGeckoNode* aNode)
+{
+ MOZ_ASSERT(aNode->IsContent());
+ nsIFrame* primaryFrame = aNode->AsContent()->GetPrimaryFrame();
+ if (!primaryFrame) {
+ return nullptr;
+ }
+
+ return primaryFrame->StyleContext();
+}
+
nsChangeHint
-Gecko_CalcAndStoreStyleDifference(RawGeckoElement* aElement,
- ServoComputedValues* aComputedValues)
+Gecko_CalcStyleDifference(nsStyleContext* aOldStyleContext,
+ ServoComputedValues* aComputedValues)
{
-#ifdef MOZ_STYLO
- nsStyleContext* oldContext = aElement->GetPrimaryFrame()->StyleContext();
+ MOZ_ASSERT(aOldStyleContext);
+ MOZ_ASSERT(aComputedValues);
// Pass the safe thing, which causes us to miss a potential optimization. See
// bug 1289863.
nsChangeHint forDescendants = nsChangeHint_Hints_NotHandledForDescendants;
// Eventually, we should compute things out of these flags like
// ElementRestyler::RestyleSelf does and pass the result to the caller to
// potentially halt traversal. See bug 1289868.
uint32_t equalStructs, samePointerStructs;
nsChangeHint result =
- oldContext->CalcStyleDifference(aComputedValues, forDescendants,
- &equalStructs, &samePointerStructs);
+ aOldStyleContext->CalcStyleDifference(aComputedValues,
+ forDescendants,
+ &equalStructs,
+ &samePointerStructs);
+
return result;
+}
+
+void
+Gecko_StoreStyleDifference(RawGeckoNode* aNode, nsChangeHint aChangeHintToStore)
+{
+#ifdef MOZ_STYLO
+ // XXXEmilio probably storing it in the nearest content parent is a sane thing
+ // to do if this case can ever happen?
+ MOZ_ASSERT(aNode->IsContent());
+
+ nsIContent* aContent = aNode->AsContent();
+ nsIFrame* primaryFrame = aContent->GetPrimaryFrame();
+ if (!primaryFrame) {
+ // TODO: Pick the undisplayed content map from the frame-constructor, and
+ // stick it there. For now we're generating ReconstructFrame
+ // unconditionally, which is suboptimal.
+ return;
+ }
+
+ primaryFrame->StyleContext()->StoreChangeHint(aChangeHintToStore);
#else
- MOZ_CRASH("stylo: Shouldn't call Gecko_CalcAndStoreStyleDifference in "
+ MOZ_CRASH("stylo: Shouldn't call Gecko_StoreStyleDifference in "
"non-stylo build");
#endif
}
ServoDeclarationBlock*
Gecko_GetServoDeclarationBlock(RawGeckoElement* aElement)
{
const nsAttrValue* attr = aElement->GetParsedAttr(nsGkAtoms::style);
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -182,18 +182,26 @@ void Gecko_SetMozBinding(nsStyleDisplay*
void Gecko_CopyMozBindingFrom(nsStyleDisplay* des, const nsStyleDisplay* src);
// Dirtiness tracking.
uint32_t Gecko_GetNodeFlags(RawGeckoNode* node);
void Gecko_SetNodeFlags(RawGeckoNode* node, uint32_t flags);
void Gecko_UnsetNodeFlags(RawGeckoNode* node, uint32_t flags);
// Incremental restyle.
-nsChangeHint Gecko_CalcAndStoreStyleDifference(RawGeckoElement* element,
- ServoComputedValues* newstyle);
+// TODO: We would avoid a few ffi calls if we decide to make an API like the
+// former CalcAndStoreStyleDifference, but that would effectively mean breaking
+// some safety guarantees in the servo side.
+//
+// Also, we might want a ComputedValues to ComputedValues API for animations?
+// Not if we do them in Gecko...
+nsStyleContext* Gecko_GetStyleContext(RawGeckoNode* node);
+nsChangeHint Gecko_CalcStyleDifference(nsStyleContext* oldstyle,
+ ServoComputedValues* newstyle);
+void Gecko_StoreStyleDifference(RawGeckoNode* node, nsChangeHint change);
// `array` must be an nsTArray
// If changing this signature, please update the
// friend function declaration in nsTArray.h
void Gecko_EnsureTArrayCapacity(void* array, size_t capacity, size_t elem_size);
void Gecko_EnsureImageLayersLength(nsStyleImageLayers* layers, size_t len);
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -80,17 +80,17 @@ nsStyleContext::nsStyleContext(nsStyleCo
, mPresContext(nullptr)
#endif
, mCachedResetData(nullptr)
, mBits(((uint64_t)aPseudoType) << NS_STYLE_CONTEXT_TYPE_SHIFT)
, mRefCnt(0)
#ifdef MOZ_STYLO
, mStoredChangeHint(nsChangeHint(0))
#ifdef DEBUG
- , mHasStoredChangeHint(false)
+ , mConsumedChangeHint(false)
#endif
#endif
#ifdef DEBUG
, mFrameRefCnt(0)
, mComputingStruct(nsStyleStructID_None)
#endif
{}
@@ -392,20 +392,16 @@ nsStyleContext::MoveTo(nsStyleContext* a
}
already_AddRefed<nsStyleContext>
nsStyleContext::FindChildWithRules(const nsIAtom* aPseudoTag,
NonOwningStyleContextSource aSource,
NonOwningStyleContextSource aSourceIfVisited,
bool aRelevantLinkVisited)
{
-#ifdef MOZ_STYLO
- MOZ_ASSERT(!mHasStoredChangeHint);
-#endif
-
uint32_t threshold = 10; // The # of siblings we're willing to examine
// before just giving this whole thing up.
RefPtr<nsStyleContext> result;
nsStyleContext *list = aSource.MatchesNoRules() ? mEmptyChild : mChild;
if (list) {
nsStyleContext *child = list;
@@ -1229,18 +1225,16 @@ nsStyleContext::CalcStyleDifference(nsSt
nsChangeHint aParentHintsNotHandledForDescendants,
uint32_t* aEqualStructs,
uint32_t* aSamePointerStructs)
{
return CalcStyleDifferenceInternal(aNewContext, aParentHintsNotHandledForDescendants,
aEqualStructs, aSamePointerStructs);
}
-#ifdef MOZ_STYLO
-
class MOZ_STACK_CLASS FakeStyleContext
{
public:
explicit FakeStyleContext(ServoComputedValues* aComputedValues)
: mComputedValues(aComputedValues) {}
mozilla::NonOwningStyleContextSource StyleSource() const {
return mozilla::NonOwningStyleContextSource(mComputedValues);
@@ -1268,17 +1262,16 @@ nsStyleContext::CalcStyleDifference(Serv
nsChangeHint aParentHintsNotHandledForDescendants,
uint32_t* aEqualStructs,
uint32_t* aSamePointerStructs)
{
FakeStyleContext newContext(aNewComputedValues);
return CalcStyleDifferenceInternal(&newContext, aParentHintsNotHandledForDescendants,
aEqualStructs, aSamePointerStructs);
}
-#endif
#ifdef DEBUG
void nsStyleContext::List(FILE* out, int32_t aIndent, bool aListDescendants)
{
nsAutoCString str;
// Indent
int32_t ix;
for (ix = aIndent; --ix >= 0; ) {
--- a/layout/style/nsStyleContext.h
+++ b/layout/style/nsStyleContext.h
@@ -393,35 +393,33 @@ public:
* aEqualStructs must not be null. Into it will be stored a bitfield
* representing which structs were compared to be non-equal.
*/
nsChangeHint CalcStyleDifference(nsStyleContext* aNewContext,
nsChangeHint aParentHintsNotHandledForDescendants,
uint32_t* aEqualStructs,
uint32_t* aSamePointerStructs);
-#ifdef MOZ_STYLO
- /*
- * Like the above, but does not require the new style context to exist yet.
- * Servo uses this to compute change hints during parallel traversal.
+ /**
+ * Like the above, but allows comparing ServoComputedValues instead of needing
+ * a full-fledged style context.
*/
nsChangeHint CalcStyleDifference(ServoComputedValues* aNewComputedValues,
nsChangeHint aParentHintsNotHandledForDescendants,
uint32_t* aEqualStructs,
uint32_t* aSamePointerStructs);
-#endif
private:
template<class StyleContextLike>
nsChangeHint CalcStyleDifferenceInternal(StyleContextLike* aNewContext,
nsChangeHint aParentHintsNotHandledForDescendants,
uint32_t* aEqualStructs,
uint32_t* aSamePointerStructs);
+
public:
-
/**
* Get a color that depends on link-visitedness using this and
* this->GetStyleIfVisited().
*
* aProperty must be a color-valued property that StyleAnimationValue
* knows how to extract. It must also be a property that we know to
* do change handling for in nsStyleContext::CalcDifference.
*
@@ -523,36 +521,52 @@ public:
cachedData = mCachedInheritedData.mStyleStructs[aSID];
}
return cachedData;
}
mozilla::NonOwningStyleContextSource StyleSource() const { return mSource.AsRaw(); }
#ifdef MOZ_STYLO
+ // NOTE: It'd be great to assert here that the previous change hint is always
+ // consumed.
+ //
+ // This is not the case right now, since the changes of childs of frames that
+ // go through frame construction are not consumed.
void StoreChangeHint(nsChangeHint aHint)
{
- MOZ_ASSERT(!mHasStoredChangeHint);
+ MOZ_ASSERT(!mConsumedChangeHint);
MOZ_ASSERT(!IsShared());
mStoredChangeHint = aHint;
#ifdef DEBUG
- mHasStoredChangeHint = true;
+ mConsumedChangeHint = false;
#endif
}
nsChangeHint ConsumeStoredChangeHint()
{
- MOZ_ASSERT(mHasStoredChangeHint);
nsChangeHint result = mStoredChangeHint;
mStoredChangeHint = nsChangeHint(0);
#ifdef DEBUG
- mHasStoredChangeHint = false;
+ mConsumedChangeHint = true;
#endif
return result;
}
+#else
+ void StoreChangeHint(nsChangeHint aHint)
+ {
+ MOZ_CRASH("stylo: Called nsStyleContext::StoreChangeHint in a non MOZ_STYLO "
+ "build.");
+ }
+
+ nsChangeHint ConsumeStoredChangeHint()
+ {
+ MOZ_CRASH("stylo: Called nsStyleContext::ComsumeStoredChangeHint in a non "
+ "MOZ_STYLO build.");
+ }
#endif
private:
// Private destructor, to discourage deletion outside of Release():
~nsStyleContext();
// Delegated Helper constructor.
nsStyleContext(nsStyleContext* aParent,
@@ -760,17 +774,17 @@ private:
uint32_t mRefCnt;
// For now we store change hints on the style context during parallel traversal.
// We should improve this - see bug 1289861.
#ifdef MOZ_STYLO
nsChangeHint mStoredChangeHint;
#ifdef DEBUG
- bool mHasStoredChangeHint;
+ bool mConsumedChangeHint;
#endif
#endif
#ifdef DEBUG
uint32_t mFrameRefCnt; // number of frames that use this
// as their style context
nsStyleStructID mComputingStruct;