зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1290335: stylo: Allow processing change hints generated from Servo. r=heycam
MozReview-Commit-ID: Alc0wcXvHcD
This commit is contained in:
Родитель
5f6238d913
Коммит
7f36b64101
|
@ -67,45 +67,78 @@ ServoRestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
|
|||
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);
|
||||
}
|
||||
|
@ -143,14 +176,21 @@ MarkChildrenAsDirtyForServo(nsIContent* aContent)
|
|||
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);
|
||||
}
|
||||
|
@ -158,27 +198,27 @@ ServoRestyleManager::NoteRestyleHint(Element* aElement, nsRestyleHint aHint)
|
|||
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();
|
||||
|
||||
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.
|
||||
|
@ -186,7 +226,6 @@ ServoRestyleManager::ProcessPendingRestyles()
|
|||
}
|
||||
|
||||
nsIDocument* doc = PresContext()->Document();
|
||||
|
||||
Element* root = doc->GetRootElement();
|
||||
if (root) {
|
||||
for (auto iter = mModifiedElements.Iter(); !iter.Done(); iter.Next()) {
|
||||
|
@ -205,7 +244,21 @@ ServoRestyleManager::ProcessPendingRestyles()
|
|||
|
||||
if (root->IsDirtyForServo() || root->HasDirtyDescendantsForServo()) {
|
||||
styleSet->RestyleSubtree(root);
|
||||
RecreateStyleContexts(root, nullptr, styleSet);
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Note this has to be *after* restyling, because otherwise frame
|
||||
// construction will find unstyled nodes, and that's not funny.
|
||||
PresContext()->FrameConstructor()->CreateNeededFrames();
|
||||
|
||||
nsStyleChangeList changeList;
|
||||
|
||||
RecreateStyleContexts(root, nullptr, styleSet, changeList);
|
||||
ProcessRestyledFrames(changeList);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -314,8 +367,10 @@ ServoRestyleManager::SnapshotForElement(Element* 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
|
||||
|
|
|
@ -36,6 +36,8 @@ class ServoRestyleManager : public RestyleManagerBase
|
|||
{
|
||||
friend class ServoStyleSet;
|
||||
public:
|
||||
typedef RestyleManagerBase base_type;
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(ServoRestyleManager)
|
||||
|
||||
explicit ServoRestyleManager(nsPresContext* aPresContext);
|
||||
|
@ -90,14 +92,11 @@ private:
|
|||
/**
|
||||
* 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.
|
||||
|
|
|
@ -55,6 +55,7 @@ public:
|
|||
|
||||
friend class mozilla::RestyleManager;
|
||||
friend class mozilla::RestyleManagerBase;
|
||||
friend class mozilla::ServoRestyleManager;
|
||||
|
||||
nsCSSFrameConstructor(nsIDocument* aDocument, nsIPresShell* aPresShell);
|
||||
~nsCSSFrameConstructor(void) {
|
||||
|
|
|
@ -40,7 +40,7 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Fills in pointers without reference counting.
|
||||
* Fills in pointers without reference counting.
|
||||
*/
|
||||
nsresult ChangeAt(int32_t aIndex, nsIFrame*& aFrame, nsIContent*& aContent,
|
||||
nsChangeHint& aHint) const;
|
||||
|
|
|
@ -184,12 +184,24 @@ Gecko_UnsetNodeFlags(RawGeckoNode* aNode, uint32_t aFlags)
|
|||
aNode->UnsetFlags(aFlags);
|
||||
}
|
||||
|
||||
nsChangeHint
|
||||
Gecko_CalcAndStoreStyleDifference(RawGeckoElement* aElement,
|
||||
ServoComputedValues* aComputedValues)
|
||||
nsStyleContext*
|
||||
Gecko_GetStyleContext(RawGeckoNode* aNode)
|
||||
{
|
||||
#ifdef MOZ_STYLO
|
||||
nsStyleContext* oldContext = aElement->GetPrimaryFrame()->StyleContext();
|
||||
MOZ_ASSERT(aNode->IsContent());
|
||||
nsIFrame* primaryFrame = aNode->AsContent()->GetPrimaryFrame();
|
||||
if (!primaryFrame) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return primaryFrame->StyleContext();
|
||||
}
|
||||
|
||||
nsChangeHint
|
||||
Gecko_CalcStyleDifference(nsStyleContext* aOldStyleContext,
|
||||
ServoComputedValues* aComputedValues)
|
||||
{
|
||||
MOZ_ASSERT(aOldStyleContext);
|
||||
MOZ_ASSERT(aComputedValues);
|
||||
|
||||
// Pass the safe thing, which causes us to miss a potential optimization. See
|
||||
// bug 1289863.
|
||||
|
@ -200,11 +212,34 @@ Gecko_CalcAndStoreStyleDifference(RawGeckoElement* aElement,
|
|||
// 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
|
||||
}
|
||||
|
|
|
@ -187,8 +187,16 @@ 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
|
||||
|
|
|
@ -85,7 +85,7 @@ nsStyleContext::nsStyleContext(nsStyleContext* aParent,
|
|||
#ifdef MOZ_STYLO
|
||||
, mStoredChangeHint(nsChangeHint(0))
|
||||
#ifdef DEBUG
|
||||
, mHasStoredChangeHint(false)
|
||||
, mConsumedChangeHint(false)
|
||||
#endif
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
|
@ -397,10 +397,6 @@ nsStyleContext::FindChildWithRules(const nsIAtom* aPseudoTag,
|
|||
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.
|
||||
|
||||
|
@ -1234,8 +1230,6 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aNewContext,
|
|||
aEqualStructs, aSamePointerStructs);
|
||||
}
|
||||
|
||||
#ifdef MOZ_STYLO
|
||||
|
||||
class MOZ_STACK_CLASS FakeStyleContext
|
||||
{
|
||||
public:
|
||||
|
@ -1273,7 +1267,6 @@ nsStyleContext::CalcStyleDifference(ServoComputedValues* aNewComputedValues,
|
|||
return CalcStyleDifferenceInternal(&newContext, aParentHintsNotHandledForDescendants,
|
||||
aEqualStructs, aSamePointerStructs);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
void nsStyleContext::List(FILE* out, int32_t aIndent, bool aListDescendants)
|
||||
|
|
|
@ -398,16 +398,14 @@ public:
|
|||
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>
|
||||
|
@ -415,8 +413,8 @@ private:
|
|||
nsChangeHint aParentHintsNotHandledForDescendants,
|
||||
uint32_t* aEqualStructs,
|
||||
uint32_t* aSamePointerStructs);
|
||||
public:
|
||||
|
||||
public:
|
||||
/**
|
||||
* Get a color that depends on link-visitedness using this and
|
||||
* this->GetStyleIfVisited().
|
||||
|
@ -528,26 +526,42 @@ public:
|
|||
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:
|
||||
|
@ -765,7 +779,7 @@ private:
|
|||
#ifdef MOZ_STYLO
|
||||
nsChangeHint mStoredChangeHint;
|
||||
#ifdef DEBUG
|
||||
bool mHasStoredChangeHint;
|
||||
bool mConsumedChangeHint;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче