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
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1454,19 +1454,16 @@ nsIDocument::nsIDocument()
mGetUserFontSetCalled(false),
mPostedFlushUserFontSet(false),
mPartID(0),
mDidFireDOMContentLoaded(true),
mHasScrollLinkedEffect(false),
mUserHasInteracted(false)
{
SetIsDocument();
- if (IsStyledByServo()) {
- SetFlags(NODE_IS_DIRTY_FOR_SERVO | NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
- }
PR_INIT_CLIST(&mDOMMediaQueryLists);
}
// NOTE! nsDocument::operator new() zeroes out all members, so don't
// bother initializing members to 0.
nsDocument::nsDocument(const char* aContentType)
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -235,17 +235,17 @@ nsXBLBinding::InstallAnonymousContent(ns
// an element is added to a XUL document), we need to notify the
// XUL document using its special API.
nsCOMPtr<nsIXULDocument> xuldoc(do_QueryInterface(doc));
if (xuldoc)
xuldoc->AddSubtreeToDocument(child);
#endif
if (servoStyleSet) {
- servoStyleSet->RestyleSubtree(child);
+ servoStyleSet->RestyleSubtree(child, /* aForce = */ true);
}
}
}
void
nsXBLBinding::UninstallAnonymousContent(nsIDocument* aDocument,
nsIContent* aAnonParent)
{
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -11,39 +11,63 @@ using namespace mozilla::dom;
namespace mozilla {
ServoRestyleManager::ServoRestyleManager(nsPresContext* aPresContext)
: RestyleManagerBase(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
ServoRestyleManager::PostRestyleEvent(Element* aElement,
nsRestyleHint aRestyleHint,
nsChangeHint aMinChangeHint)
{
if (MOZ_UNLIKELY(IsDisconnected()) ||
MOZ_UNLIKELY(PresContext()->PresShell()->IsDestroying())) {
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())
break;
cur->SetHasDirtyDescendantsForServo();
}
presShell->GetDocument()->SetNeedStyleFlush();
@@ -64,20 +88,69 @@ ServoRestyleManager::RebuildAllStyleData
void
ServoRestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
nsRestyleHint aRestyleHint)
{
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();
}
void
ServoRestyleManager::RestyleForInsertOrChange(Element* aContainer,
nsIContent* aChild)
{
NS_ERROR("stylo: ServoRestyleManager::RestyleForInsertOrChange not implemented");
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.h
@@ -26,22 +26,22 @@ class nsIFrame;
namespace mozilla {
/**
* Restyle manager for a Servo-backed style system.
*/
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);
void PostRestyleEventForLazyConstruction();
void RebuildAllStyleData(nsChangeHint aExtraHint,
nsRestyleHint aRestyleHint);
void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
nsRestyleHint aRestyleHint);
@@ -67,16 +67,34 @@ public:
const nsAttrValue* aOldValue);
nsresult ReparentStyleContext(nsIFrame* aFrame);
bool HasPendingRestyles();
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 "
"style backend");
return PresContext()->StyleSet()->AsServo();
}
};
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -4257,17 +4257,17 @@ nsCSSFrameConstructor::GetAnonymousConte
content->UnbindFromTree();
return rv;
}
}
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);
}
}
return NS_OK;
}
static
bool IsXULDisplayType(const nsStyleDisplay* aDisplay)
--- a/layout/base/nsRefreshDriver.h
+++ b/layout/base/nsRefreshDriver.h
@@ -150,17 +150,17 @@ public:
bool AddImageRequest(imgIRequest* aRequest);
void RemoveImageRequest(imgIRequest* aRequest);
/**
* Add / remove presshells that we should flush style and layout on
*/
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
// near the call site where the first observer is triggered.
if (!mStyleCause) {
mStyleCause = profiler_get_backtrace();
}
bool appended = mStyleFlushObservers.AppendElement(aShell) != nullptr;
@@ -168,17 +168,17 @@ public:
return appended;
}
void RemoveStyleFlushObserver(nsIPresShell* aShell) {
mStyleFlushObservers.RemoveElement(aShell);
}
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
// near the call site where the first observer is triggered.
if (!mReflowCause) {
mReflowCause = profiler_get_backtrace();
}
bool appended = mLayoutFlushObservers.AppendElement(aShell) != nullptr;
@@ -188,17 +188,17 @@ public:
void RemoveLayoutFlushObserver(nsIPresShell* aShell) {
mLayoutFlushObservers.RemoveElement(aShell);
}
bool IsLayoutFlushObserver(nsIPresShell* aShell) {
return mLayoutFlushObservers.Contains(aShell);
}
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;
}
void RemovePresShellToInvalidateIfHidden(nsIPresShell* aShell) {
mPresShellsToInvalidateIfHidden.RemoveElement(aShell);
}
@@ -283,17 +283,17 @@ public:
*/
static void PVsyncActorCreated(mozilla::layout::VsyncChild* aVsyncChild);
#ifdef DEBUG
/**
* Check whether the given observer is an observer for the given flush type
*/
bool IsRefreshObserver(nsARefreshObserver *aObserver,
- mozFlushType aFlushType);
+ mozFlushType aFlushType);
#endif
/**
* Default interval the refresh driver uses, in ms.
*/
static int32_t DefaultInterval();
bool IsInRefresh() { return mInRefresh; }
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -1,16 +1,17 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ServoStyleSet.h"
+#include "mozilla/ServoRestyleManager.h"
#include "nsCSSAnonBoxes.h"
#include "nsCSSPseudoElements.h"
#include "nsIDocumentInlines.h"
#include "nsStyleContext.h"
#include "nsStyleSet.h"
using namespace mozilla;
using namespace mozilla::dom;
@@ -374,12 +375,17 @@ ServoStyleSet::HasStateDependentStyle(do
CSSPseudoElementType aPseudoType,
dom::Element* aPseudoElement,
EventStates aStateMask)
{
MOZ_CRASH("stylo: not implemented");
}
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());
}
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -19,31 +19,33 @@
#include "nsIAtom.h"
#include "nsTArray.h"
namespace mozilla {
namespace dom {
class Element;
} // namespace dom
class CSSStyleSheet;
+class ServoRestyleManager;
class ServoStyleSheet;
} // namespace mozilla
class nsIDocument;
class nsStyleContext;
class nsPresContext;
struct TreeMatchContext;
namespace mozilla {
/**
* The set of style sheets that apply to a document, backed by a Servo
* Stylist. A ServoStyleSet contains ServoStyleSheets.
*/
class ServoStyleSet
{
+ friend class ServoRestyleManager;
public:
ServoStyleSet();
void Init(nsPresContext* aPresContext);
void BeginShutdown();
void Shutdown();
bool GetAuthorStyleDisabled() const;
@@ -111,17 +113,23 @@ public:
// Test if style is dependent on content state
nsRestyleHint HasStateDependentStyle(dom::Element* aElement,
EventStates aStateMask);
nsRestyleHint HasStateDependentStyle(dom::Element* aElement,
mozilla::CSSPseudoElementType aPseudoType,
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>,
nsStyleContext* aParentContext,
nsIAtom* aPseudoTag,
CSSPseudoElementType aPseudoType);
already_AddRefed<nsStyleContext> GetContext(nsIContent* aContent,