Bug 1290335: stylo: Allow processing change hints generated from Servo. r=heycam

MozReview-Commit-ID: Alc0wcXvHcD
This commit is contained in:
Emilio Cobos Álvarez 2016-07-28 19:20:45 -07:00
Родитель 5f6238d913
Коммит 7f36b64101
8 изменённых файлов: 172 добавлений и 67 удалений

Просмотреть файл

@ -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