Bug 1286445: stylo: Support restyles of non-pseudo content on state change. r=heycam

This includes, for example :hover.

Also removes the call to IsStyledByServo() in the document constructor, it's not
only unnecessary, but also we call UpdateStyleBackendType() too early.

MozReview-Commit-ID: 4YfCdmLoSxu
This commit is contained in:
Emilio Cobos Álvarez 2016-07-13 13:42:47 -07:00
Родитель 2adb931c36
Коммит 1a49c35a07
8 изменённых файлов: 119 добавлений и 17 удалений

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

@ -1459,9 +1459,6 @@ nsIDocument::nsIDocument()
mUserHasInteracted(false)
{
SetIsDocument();
if (IsStyledByServo()) {
SetFlags(NODE_IS_DIRTY_FOR_SERVO | NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
}
PR_INIT_CLIST(&mDOMMediaQueryLists);
}

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

@ -240,7 +240,7 @@ nsXBLBinding::InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElem
#endif
if (servoStyleSet) {
servoStyleSet->RestyleSubtree(child);
servoStyleSet->RestyleSubtree(child, /* aForce = */ true);
}
}
}

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

@ -16,10 +16,26 @@ ServoRestyleManager::ServoRestyleManager(nsPresContext* aPresContext)
{
}
void
ServoRestyleManager::Disconnect()
/* static */ void
ServoRestyleManager::DirtyTree(nsIContent* aContent)
{
NS_ERROR("stylo: ServoRestyleManager::Disconnect not implemented");
if (aContent->IsDirtyForServo()) {
return;
}
aContent->SetIsDirtyForServo();
FlattenedChildIterator it(aContent);
nsIContent* n = it.GetNextChild();
bool hadChildren = bool(n);
for ( ; n; n = it.GetNextChild()) {
DirtyTree(n);
}
if (hadChildren) {
aContent->SetHasDirtyDescendantsForServo();
}
}
void
@ -32,13 +48,21 @@ ServoRestyleManager::PostRestyleEvent(Element* aElement,
return;
}
if (aRestyleHint == 0 && !aMinChangeHint) {
// Nothing to do here
return;
}
nsIPresShell* presShell = PresContext()->PresShell();
if (!ObservingRefreshDriver()) {
SetObservingRefreshDriver(PresContext()->RefreshDriver()->
AddStyleFlushObserver(presShell));
}
aElement->SetIsDirtyForServo();
// Propagate the IS_DIRTY flag down the tree.
DirtyTree(aElement);
// Propagate the HAS_DIRTY_DESCENDANTS flag to the root.
nsINode* cur = aElement;
while ((cur = cur->GetParentNode())) {
if (cur->HasDirtyDescendantsForServo())
@ -69,10 +93,59 @@ ServoRestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
MOZ_CRASH("stylo: ServoRestyleManager::PostRebuildAllStyleDataEvent not implemented");
}
/* static */ void
ServoRestyleManager::RecreateStyleContexts(nsIContent* aContent,
nsStyleContext* aParentContext,
ServoStyleSet* aStyleSet)
{
if (!(aContent->IsDirtyForServo() || aContent->HasDirtyDescendantsForServo())) {
return;
}
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) {
return;
}
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 =
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());
FlattenedChildIterator it(aContent);
for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
RecreateStyleContexts(n, context.get(), aStyleSet);
}
}
void
ServoRestyleManager::ProcessPendingRestyles()
{
// XXXheycam Do nothing for now.
ServoStyleSet* styleSet = StyleSet();
nsIDocument* doc = PresContext()->Document();
Element* root = doc->GetRootElement();
if (root) {
styleSet->RestyleSubtree(root, /* aForce = */ false);
RecreateStyleContexts(root, nullptr, styleSet);
}
IncrementRestyleGeneration();
}

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

@ -31,12 +31,12 @@ namespace mozilla {
*/
class ServoRestyleManager : public RestyleManagerBase
{
friend class ServoStyleSet;
public:
NS_INLINE_DECL_REFCOUNTING(ServoRestyleManager)
explicit ServoRestyleManager(nsPresContext* aPresContext);
void Disconnect();
void PostRestyleEvent(dom::Element* aElement,
nsRestyleHint aRestyleHint,
nsChangeHint aMinChangeHint);
@ -72,6 +72,24 @@ protected:
~ServoRestyleManager() {}
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);
/**
* Propagates the IS_DIRTY flag down to the tree, setting
* HAS_DIRTY_DESCENDANTS appropriately.
*/
static void DirtyTree(nsIContent* aContent);
inline ServoStyleSet* StyleSet() const {
MOZ_ASSERT(PresContext()->StyleSet()->IsServo(),
"ServoRestyleManager should only be used with a Servo-flavored "

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

@ -4262,7 +4262,7 @@ nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent,
if (ServoStyleSet* styleSet = mPresShell->StyleSet()->GetAsServo()) {
// Eagerly compute styles for the anonymous content tree.
for (auto& info : aContent) {
styleSet->RestyleSubtree(info.mContent);
styleSet->RestyleSubtree(info.mContent, /* aForce = */ true);
}
}

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

@ -155,7 +155,7 @@ public:
*/
bool AddStyleFlushObserver(nsIPresShell* aShell) {
NS_ASSERTION(!mStyleFlushObservers.Contains(aShell),
"Double-adding style flush observer");
"Double-adding style flush observer");
// We only get the cause for the first observer each frame because capturing
// a stack is expensive. This is still useful if (1) you're trying to remove
// all flushes for a particial frame or (2) the costly flush is triggered
@ -173,7 +173,7 @@ public:
}
bool AddLayoutFlushObserver(nsIPresShell* aShell) {
NS_ASSERTION(!IsLayoutFlushObserver(aShell),
"Double-adding layout flush observer");
"Double-adding layout flush observer");
// We only get the cause for the first observer each frame because capturing
// a stack is expensive. This is still useful if (1) you're trying to remove
// all flushes for a particial frame or (2) the costly flush is triggered
@ -193,7 +193,7 @@ public:
}
bool AddPresShellToInvalidateIfHidden(nsIPresShell* aShell) {
NS_ASSERTION(!mPresShellsToInvalidateIfHidden.Contains(aShell),
"Double-adding style flush observer");
"Double-adding style flush observer");
bool appended = mPresShellsToInvalidateIfHidden.AppendElement(aShell) != nullptr;
EnsureTimerStarted();
return appended;
@ -288,7 +288,7 @@ public:
* Check whether the given observer is an observer for the given flush type
*/
bool IsRefreshObserver(nsARefreshObserver *aObserver,
mozFlushType aFlushType);
mozFlushType aFlushType);
#endif
/**

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

@ -6,6 +6,7 @@
#include "mozilla/ServoStyleSet.h"
#include "mozilla/ServoRestyleManager.h"
#include "nsCSSAnonBoxes.h"
#include "nsCSSPseudoElements.h"
#include "nsIDocumentInlines.h"
@ -379,7 +380,12 @@ ServoStyleSet::HasStateDependentStyle(dom::Element* aElement,
}
void
ServoStyleSet::RestyleSubtree(nsINode* aNode)
ServoStyleSet::RestyleSubtree(nsINode* aNode, bool aForce)
{
if (aForce) {
MOZ_ASSERT(aNode->IsContent());
ServoRestyleManager::DirtyTree(aNode->AsContent());
}
Servo_RestyleSubtree(aNode, mRawSet.get());
}

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

@ -24,6 +24,7 @@ namespace dom {
class Element;
} // namespace dom
class CSSStyleSheet;
class ServoRestyleManager;
class ServoStyleSheet;
} // namespace mozilla
class nsIDocument;
@ -39,6 +40,7 @@ namespace mozilla {
*/
class ServoStyleSet
{
friend class ServoRestyleManager;
public:
ServoStyleSet();
@ -116,7 +118,13 @@ public:
dom::Element* aPseudoElement,
EventStates aStateMask);
void RestyleSubtree(nsINode* aNode);
/**
* Restyles a whole subtree of nodes.
*
* The aForce parameter propagates the dirty bits down the subtree, and when
* used aNode needs to be nsIContent.
*/
void RestyleSubtree(nsINode* aNode, bool aForce);
private:
already_AddRefed<nsStyleContext> GetContext(already_AddRefed<ServoComputedValues>,