Bug 1367553: Move dirty style tracking to the StyleSet. r=heycam
MozReview-Commit-ID: 2ut8SAwNGN2
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -47,17 +47,16 @@
#include "gfxPrefs.h"
#include "gfxUserFontSet.h"
#include "nsPresContext.h"
#include "nsIContent.h"
#include "nsIContentIterator.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h" // for Event::GetEventPopupControlState()
-#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/dom/PointerEvent.h"
#include "nsIDocument.h"
#include "nsAnimationManager.h"
#include "nsNameSpaceManager.h" // for Pref-related rule management (bugs 22963,20760,31816)
#include "nsFrame.h"
#include "FrameLayerBuilder.h"
#include "nsViewManager.h"
#include "nsView.h"
@@ -779,17 +778,16 @@ nsIPresShell::nsIPresShell()
#ifdef DEBUG
, mDrawEventTargetFrame(nullptr)
#endif
, mPaintCount(0)
, mAutoWeakFrames(nullptr)
, mCanvasBackgroundColor(NS_RGBA(0,0,0,0))
, mSelectionFlags(0)
, mRenderFlags(0)
- , mStylesHaveChanged(false)
, mDidInitialize(false)
, mIsDestroying(false)
, mIsReflowing(false)
, mPaintingSuppressed(false)
, mIsThemeSupportDisabled(false)
, mIsActive(false)
, mFrozen(false)
, mIsFirstPaint(false)
@@ -1539,17 +1537,16 @@ PresShell::AddUserSheet(StyleSheet* aShe
// Now iterate backwards, so that the order of userSheets will be the same as
// the order of sheets from it in the style set.
for (StyleSheet* sheet : Reversed(userSheets)) {
mStyleSet->PrependStyleSheet(SheetType::User, sheet);
}
mStyleSet->EndUpdate();
-
RestyleForCSSRuleChanges();
}
void
PresShell::AddAgentSheet(StyleSheet* aSheet)
{
// Make sure this does what nsDocumentViewer::CreateStyleSet does
// wrt ordering.
@@ -2530,18 +2527,19 @@ PresShell::EndUpdate(nsIDocument *aDocum
{
#ifdef DEBUG
NS_PRECONDITION(0 != mUpdateCount, "too many EndUpdate's");
--mUpdateCount;
#endif
if (aUpdateType & UPDATE_STYLE) {
mStyleSet->EndUpdate();
- if (mStylesHaveChanged || !mChangedScopeStyleRoots.IsEmpty())
+ if (mStyleSet->StyleSheetsHaveChanged()) {
RestyleForCSSRuleChanges();
+ }
}
mFrameConstructor->EndUpdate();
}
void
PresShell::RestoreRootScrollPosition()
{
@@ -4541,93 +4539,43 @@ PresShell::ReconstructFrames()
mFrameConstructor->ReconstructDocElementHierarchy();
VERIFY_STYLE_TREE;
mFrameConstructor->EndUpdate();
}
void
nsIPresShell::RestyleForCSSRuleChanges()
{
- AutoTArray<RefPtr<mozilla::dom::Element>,1> scopeRoots;
- mChangedScopeStyleRoots.SwapElements(scopeRoots);
-
- if (mStylesHaveChanged) {
- // If we need to restyle everything, no need to restyle individual
- // scoped style roots.
- scopeRoots.Clear();
- }
-
- mStylesHaveChanged = false;
-
if (mIsDestroying) {
// We don't want to mess with restyles at this point
return;
}
mDocument->RebuildUserFontSet();
if (mPresContext) {
mPresContext->RebuildCounterStyles();
}
- Element* root = mDocument->GetRootElement();
if (!mDidInitialize) {
// Nothing to do here, since we have no frames yet
return;
}
- if (!root) {
- // No content to restyle
- return;
- }
-
- RestyleManager* restyleManager = mPresContext->RestyleManager();
-
- if (scopeRoots.IsEmpty()) {
- // If scopeRoots is empty, we know that mStylesHaveChanged was true at
- // the beginning of this function, and that we need to restyle the whole
- // document.
- restyleManager->PostRestyleEventForCSSRuleChanges(root,
- eRestyle_Subtree,
- nsChangeHint(0));
- } else {
- for (Element* scopeRoot : scopeRoots) {
- restyleManager->PostRestyleEventForCSSRuleChanges(scopeRoot,
- eRestyle_Subtree,
- nsChangeHint(0));
- }
- }
+ mStyleSet->InvalidateStyleForCSSRuleChanges();
}
void
PresShell::RecordStyleSheetChange(StyleSheet* aStyleSheet)
{
// too bad we can't check that the update is UPDATE_STYLE
NS_ASSERTION(mUpdateCount != 0, "must be in an update");
-
- if (mStylesHaveChanged)
- return;
-
- // Tell Servo that the contents of style sheets have changed.
- if (ServoStyleSet* set = mStyleSet->GetAsServo()) {
- set->NoteStyleSheetsChanged();
- }
-
- if (aStyleSheet->IsGecko()) {
- // XXXheycam ServoStyleSheets don't support <style scoped> yet.
- Element* scopeElement = aStyleSheet->AsGecko()->GetScopeElement();
- if (scopeElement) {
- mChangedScopeStyleRoots.AppendElement(scopeElement);
- return;
- }
- } else {
- NS_WARNING("stylo: ServoStyleSheets don't support <style scoped>");
- }
-
- mStylesHaveChanged = true;
+ MOZ_ASSERT(aStyleSheet->IsServo() == mStyleSet->IsServo());
+
+ mStyleSet->RecordStyleSheetChange(aStyleSheet);
}
void
PresShell::StyleSheetAdded(StyleSheet* aStyleSheet,
bool aDocumentSheet)
{
// We only care when enabled sheets are added
NS_PRECONDITION(aStyleSheet, "Must have a style sheet!");
@@ -6347,17 +6295,17 @@ public:
private:
PresShell* mShell;
uint32_t mFlags;
};
void
PresShell::RecordShadowStyleChange(ShadowRoot* aShadowRoot)
{
- mChangedScopeStyleRoots.AppendElement(aShadowRoot->GetHost()->AsElement());
+ mStyleSet->RecordShadowStyleChange(aShadowRoot);
}
void
PresShell::Paint(nsView* aViewToPaint,
const nsRegion& aDirtyRegion,
uint32_t aFlags)
{
#ifdef MOZ_GECKO_PROFILER
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1857,21 +1857,16 @@ protected:
int16_t mSelectionFlags;
// Flags controlling how our document is rendered. These persist
// between paints and so are tied with retained layer pixels.
// PresShell flushes retained layers when the rendering state
// changes in a way that prevents us from being able to (usefully)
// re-use old pixels.
RenderFlags mRenderFlags;
-
- // Indicates that the whole document must be restyled. Changes to scoped
- // style sheets are recorded in mChangedScopeStyleRoots rather than here
- // in mStylesHaveChanged.
- bool mStylesHaveChanged : 1;
bool mDidInitialize : 1;
bool mIsDestroying : 1;
bool mIsReflowing : 1;
// For all documents we initially lock down painting.
bool mPaintingSuppressed : 1;
// Whether or not form controls should use nsITheme in this shell.
@@ -1901,25 +1896,16 @@ protected:
bool mObservingLayoutFlushes: 1;
// True if there are throttled animations that would be processed when
// performing a flush with mFlushAnimations == true.
bool mNeedThrottledAnimationFlush : 1;
uint32_t mPresShellId;
- // List of subtrees rooted at style scope roots that need to be restyled.
- // When a change to a scoped style sheet is made, we add the style scope
- // root to this array rather than setting mStylesHaveChanged = true, since
- // we know we don't need to restyle the whole document. However, if in the
- // same update block we have already had other changes that require
- // the whole document to be restyled (i.e., mStylesHaveChanged is already
- // true), then we don't bother adding the scope root here.
- AutoTArray<RefPtr<mozilla::dom::Element>,1> mChangedScopeStyleRoots;
-
static nsIContent* gKeyDownTarget;
// Cached font inflation values. This is done to prevent changing of font
// inflation until a page is reloaded.
uint32_t mFontSizeInflationEmPerLine;
uint32_t mFontSizeInflationMinTwips;
uint32_t mFontSizeInflationLineThreshold;
bool mFontSizeInflationForceEnabled;
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -120,16 +120,25 @@ void
ServoStyleSet::Shutdown()
{
// Make sure we drop our cached style contexts before the presshell arena
// starts going away.
ClearNonInheritingStyleContexts();
mRawSet = nullptr;
}
+void
+ServoStyleSet::InvalidateStyleForCSSRuleChanges()
+{
+ if (Element* root = mPresContext->Document()->GetRootElement()) {
+ mPresContext->RestyleManager()->PostRestyleEventForCSSRuleChanges(
+ root, eRestyle_Subtree, nsChangeHint(0));
+ }
+}
+
size_t
ServoStyleSet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t n = aMallocSizeOf(this);
// Measurement of the following members may be added later if DMD finds it is
// worthwhile:
// - mRawSet
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -100,16 +100,34 @@ public:
ServoStyleSet();
~ServoStyleSet();
void Init(nsPresContext* aPresContext);
void BeginShutdown();
void Shutdown();
+ void RecordStyleSheetChange(mozilla::ServoStyleSheet*)
+ {
+ NoteStyleSheetsChanged();
+ }
+
+ void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot) {
+ // FIXME(emilio): When we properly support shadow dom we'll need to do
+ // better.
+ NoteStyleSheetsChanged();
+ }
+
+ bool StyleSheetsHaveChanged() const
+ {
+ return StylistNeedsUpdate();
+ }
+
+ void InvalidateStyleForCSSRuleChanges();
+
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
const RawServoStyleSet& RawSet() const { return *mRawSet; }
bool GetAuthorStyleDisabled() const;
nsresult SetAuthorStyleDisabled(bool aStyleDisabled);
void BeginUpdate();
nsresult EndUpdate();
--- a/layout/style/StyleSetHandle.h
+++ b/layout/style/StyleSetHandle.h
@@ -17,16 +17,17 @@
#include "nsCSSPseudoElements.h"
#include "nsTArray.h"
namespace mozilla {
class CSSStyleSheet;
class ServoStyleSet;
namespace dom {
class Element;
+class ShadowRoot;
} // namespace dom
} // namespace mozilla
class nsCSSCounterStyleRule;
struct nsFontFaceRuleContainer;
class nsIAtom;
class nsIContent;
class nsIDocument;
class nsStyleContext;
@@ -148,16 +149,20 @@ public:
const nsTArray<RefPtr<StyleSheet>>& aNewSheets);
inline nsresult InsertStyleSheetBefore(SheetType aType,
StyleSheet* aNewSheet,
StyleSheet* aReferenceSheet);
inline int32_t SheetCount(SheetType aType) const;
inline StyleSheet* StyleSheetAt(SheetType aType, int32_t aIndex) const;
inline nsresult RemoveDocStyleSheet(StyleSheet* aSheet);
inline nsresult AddDocStyleSheet(StyleSheet* aSheet, nsIDocument* aDocument);
+ inline void RecordStyleSheetChange(StyleSheet* aSheet);
+ inline void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot);
+ inline bool StyleSheetsHaveChanged() const;
+ inline void InvalidateStyleForCSSRuleChanges();
inline already_AddRefed<nsStyleContext>
ProbePseudoElementStyle(dom::Element* aParentElement,
mozilla::CSSPseudoElementType aType,
nsStyleContext* aParentContext);
inline already_AddRefed<nsStyleContext>
ProbePseudoElementStyle(dom::Element* aParentElement,
mozilla::CSSPseudoElementType aType,
nsStyleContext* aParentContext,
--- a/layout/style/StyleSetHandleInlines.h
+++ b/layout/style/StyleSetHandleInlines.h
@@ -215,16 +215,41 @@ StyleSetHandle::Ptr::RemoveDocStyleSheet
nsresult
StyleSetHandle::Ptr::AddDocStyleSheet(StyleSheet* aSheet,
nsIDocument* aDocument)
{
FORWARD_CONCRETE(AddDocStyleSheet, (aSheet->AsGecko(), aDocument),
(aSheet->AsServo(), aDocument));
}
+void
+StyleSetHandle::Ptr::RecordStyleSheetChange(StyleSheet* aSheet)
+{
+ FORWARD_CONCRETE(RecordStyleSheetChange, (aSheet->AsGecko()),
+ (aSheet->AsServo()));
+}
+
+void
+StyleSetHandle::Ptr::RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot)
+{
+ FORWARD(RecordShadowStyleChange, (aShadowRoot));
+}
+
+bool
+StyleSetHandle::Ptr::StyleSheetsHaveChanged() const
+{
+ FORWARD(StyleSheetsHaveChanged, ());
+}
+
+void
+StyleSetHandle::Ptr::InvalidateStyleForCSSRuleChanges()
+{
+ FORWARD(InvalidateStyleForCSSRuleChanges, ());
+}
+
// check whether there is ::before/::after style for an element
already_AddRefed<nsStyleContext>
StyleSetHandle::Ptr::ProbePseudoElementStyle(dom::Element* aParentElement,
CSSPseudoElementType aType,
nsStyleContext* aParentContext)
{
FORWARD(ProbePseudoElementStyle, (aParentElement, aType, aParentContext));
}
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -28,16 +28,17 @@
#include "nsCSSRuleProcessor.h"
#include "nsDataHashtable.h"
#include "nsIContent.h"
#include "nsRuleData.h"
#include "nsRuleProcessorData.h"
#include "nsAnimationManager.h"
#include "nsStyleSheetService.h"
#include "mozilla/dom/Element.h"
+#include "mozilla/dom/ShadowRoot.h"
#include "GeckoProfiler.h"
#include "nsHTMLCSSStyleSheet.h"
#include "nsHTMLStyleSheet.h"
#include "nsCSSRules.h"
#include "nsPrintfCString.h"
#include "nsIFrame.h"
#include "mozilla/RestyleManager.h"
#include "mozilla/RestyleManagerInlines.h"
@@ -205,16 +206,17 @@ nsStyleSet::IsCSSSheetType(SheetType aSh
}
}
return false;
}
nsStyleSet::nsStyleSet()
: mRuleTree(nullptr),
mBatching(0),
+ mStylesHaveChanged(0),
mInShutdown(false),
mInGC(false),
mAuthorStyleDisabled(false),
mInReconstruct(false),
mInitFontFeatureValuesLookup(true),
mNeedsRestyleAfterEnsureUniqueInner(false),
mDirty(0),
mRootStyleContextCount(0),
@@ -2361,16 +2363,78 @@ nsStyleSet::Shutdown()
// starts going away.
ClearNonInheritingStyleContexts();
mRuleTree = nullptr;
GCRuleTrees();
MOZ_ASSERT(mUnusedRuleNodeList.isEmpty());
MOZ_ASSERT(mUnusedRuleNodeCount == 0);
}
+void
+nsStyleSet::RecordStyleSheetChange(CSSStyleSheet* aStyleSheet)
+{
+ MOZ_ASSERT(mBatching != 0, "Should be in an update");
+
+ if (mStylesHaveChanged) {
+ return;
+ }
+
+ if (Element* scopeElement = aStyleSheet->GetScopeElement()) {
+ mChangedScopeStyleRoots.AppendElement(scopeElement);
+ return;
+ }
+
+ mStylesHaveChanged = true;
+ // If we need to restyle everything, no need to restyle individual
+ // scoped style roots.
+ mChangedScopeStyleRoots.Clear();
+}
+
+void
+nsStyleSet::RecordShadowStyleChange(ShadowRoot* aShadowRoot)
+{
+ if (mStylesHaveChanged) {
+ return;
+ }
+
+ mChangedScopeStyleRoots.AppendElement(aShadowRoot->GetHost()->AsElement());
+}
+
+void
+nsStyleSet::InvalidateStyleForCSSRuleChanges()
+{
+ MOZ_ASSERT_IF(mStylesHaveChanged, mChangedScopeStyleRoots.IsEmpty());
+
+ AutoTArray<RefPtr<mozilla::dom::Element>, 1> scopeRoots;
+ mChangedScopeStyleRoots.SwapElements(scopeRoots);
+ mStylesHaveChanged = false;
+
+ nsPresContext* presContext = PresContext();
+ RestyleManager* restyleManager = presContext->RestyleManager();
+ Element* root = presContext->Document()->GetRootElement();
+ if (!root) {
+ // No content to restyle
+ return;
+ }
+
+ if (scopeRoots.IsEmpty()) {
+ // If scopeRoots is empty, we know that mStylesHaveChanged was true at
+ // the beginning of this function, and that we need to restyle the whole
+ // document.
+ restyleManager->PostRestyleEventForCSSRuleChanges(root,
+ eRestyle_Subtree,
+ nsChangeHint(0));
+ } else {
+ for (Element* scopeRoot : scopeRoots) {
+ restyleManager->PostRestyleEventForCSSRuleChanges(scopeRoot,
+ eRestyle_Subtree,
+ nsChangeHint(0));
+ }
+ }
+}
void
nsStyleSet::GCRuleTrees()
{
MOZ_ASSERT(!mInReconstruct);
MOZ_ASSERT(!mInGC);
mInGC = true;
--- a/layout/style/nsStyleSet.h
+++ b/layout/style/nsStyleSet.h
@@ -36,18 +36,21 @@ class nsCSSCounterStyleRule;
class nsICSSPseudoComparator;
class nsRuleWalker;
struct ElementDependentRuleProcessorData;
struct nsFontFaceRuleContainer;
struct TreeMatchContext;
namespace mozilla {
class CSSStyleSheet;
+enum class CSSPseudoElementType : uint8_t;
class EventStates;
-enum class CSSPseudoElementType : uint8_t;
+namespace dom {
+class ShadowRoot;
+} // namespace dom
} // namespace mozilla
class nsEmptyStyleRule final : public nsIStyleRule
{
private:
~nsEmptyStyleRule() {}
public:
@@ -324,16 +327,29 @@ class nsStyleSet final
// Begin ignoring style context destruction, to avoid lots of unnecessary
// work on document teardown.
void BeginShutdown();
// Free all of the data associated with this style set.
void Shutdown();
+ // Notes that a style sheet has changed.
+ void RecordStyleSheetChange(mozilla::CSSStyleSheet* aStyleSheet);
+
+ // Notes that style sheets have changed in a shadow root.
+ void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot);
+
+ bool StyleSheetsHaveChanged() const
+ {
+ return mStylesHaveChanged || !mChangedScopeStyleRoots.IsEmpty();
+ }
+
+ void InvalidateStyleForCSSRuleChanges();
+
// Get a new style context that lives in a different parent
// The new context will be the same as the old if the new parent is the
// same as the old parent.
// aElement should be non-null if this is a style context for an
// element or pseudo-element; in the latter case it should be the
// real element the pseudo-element is for.
already_AddRefed<nsStyleContext>
ReparentStyleContext(nsStyleContext* aStyleContext,
@@ -598,18 +614,31 @@ private:
nsTArray<nsCOMPtr<nsIStyleRuleProcessor> > mScopedDocSheetRuleProcessors;
RefPtr<nsBindingManager> mBindingManager;
RefPtr<nsRuleNode> mRuleTree; // This is the root of our rule tree. It is a
// lexicographic tree of matched rules that style
// contexts use to look up properties.
+ // List of subtrees rooted at style scope roots that need to be restyled.
+ // When a change to a scoped style sheet is made, we add the style scope
+ // root to this array rather than setting mStylesHaveChanged = true, since
+ // we know we don't need to restyle the whole document. However, if in the
+ // same update block we have already had other changes that require
+ // the whole document to be restyled (i.e., mStylesHaveChanged is already
+ // true), then we don't bother adding the scope root here.
+ AutoTArray<RefPtr<mozilla::dom::Element>,1> mChangedScopeStyleRoots;
+
uint16_t mBatching;
+ // Indicates that the whole document must be restyled. Changes to scoped
+ // style sheets are recorded in mChangedScopeStyleRoots rather than here
+ // in mStylesHaveChanged.
+ unsigned mStylesHaveChanged : 1;
unsigned mInShutdown : 1;
unsigned mInGC : 1;
unsigned mAuthorStyleDisabled: 1;
unsigned mInReconstruct : 1;
unsigned mInitFontFeatureValuesLookup : 1;
unsigned mNeedsRestyleAfterEnsureUniqueInner : 1;
unsigned mDirty : int(mozilla::SheetType::Count); // one bit per sheet type