Commit e13cdb60 authored by Fergal Daly's avatar Fergal Daly Committed by Commit Bot

CSS: Documentation for style invalidation process.

This adds style-invalidation.md and also several in-code comments that
go along with it.

Change-Id: If0cfbae69ae23652d617d7fa195611134f8a8fe3
Reviewed-on: https://chromium-review.googlesource.com/1086679
Commit-Queue: Fergal Daly <fergal@chromium.org>
Reviewed-by: default avatarRune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#565997}
parent f998ff02
...@@ -12,4 +12,4 @@ Overview is [here](style-calculation.md). ...@@ -12,4 +12,4 @@ Overview is [here](style-calculation.md).
## <a name="style-invalidation"></a> Style Invalidation ## <a name="style-invalidation"></a> Style Invalidation
Style invalidation docs <https://goo.gl/3ane6s> and <https://goo.gl/z0Z9gn>. Overview and further links are [here](style-invalidation.md).
...@@ -68,6 +68,8 @@ class CORE_EXPORT PendingInvalidations { ...@@ -68,6 +68,8 @@ class CORE_EXPORT PendingInvalidations {
PendingInvalidations(); PendingInvalidations();
~PendingInvalidations(){}; ~PendingInvalidations(){};
void Invalidate(Document&); void Invalidate(Document&);
// May immediately invalidate the node and/or add pending invalidation sets to
// this node.
void ScheduleInvalidationSetsForNode(const InvalidationLists&, void ScheduleInvalidationSetsForNode(const InvalidationLists&,
ContainerNode&); ContainerNode&);
void ScheduleSiblingInvalidationsAsDescendants( void ScheduleSiblingInvalidationsAsDescendants(
......
...@@ -41,6 +41,10 @@ struct InvalidationLists; ...@@ -41,6 +41,10 @@ struct InvalidationLists;
class QualifiedName; class QualifiedName;
class RuleData; class RuleData;
// Summarizes and indexes the contents of RuleData objects. It creates
// invalidation sets from rule data and makes them available via several
// CollectInvalidationSetForFoo methods which use the indices to quickly gather
// the relevant InvalidationSets for a particular DOM mutation.
class CORE_EXPORT RuleFeatureSet { class CORE_EXPORT RuleFeatureSet {
DISALLOW_NEW(); DISALLOW_NEW();
...@@ -48,6 +52,7 @@ class CORE_EXPORT RuleFeatureSet { ...@@ -48,6 +52,7 @@ class CORE_EXPORT RuleFeatureSet {
RuleFeatureSet(); RuleFeatureSet();
~RuleFeatureSet(); ~RuleFeatureSet();
// Methods for updating the data in this object.
void Add(const RuleFeatureSet&); void Add(const RuleFeatureSet&);
void Clear(); void Clear();
...@@ -55,6 +60,7 @@ class CORE_EXPORT RuleFeatureSet { ...@@ -55,6 +60,7 @@ class CORE_EXPORT RuleFeatureSet {
SelectorPreMatch CollectFeaturesFromRuleData(const RuleData&); SelectorPreMatch CollectFeaturesFromRuleData(const RuleData&);
// Methods for accessing the data in this object.
bool UsesFirstLineRules() const { return metadata_.uses_first_line_rules; } bool UsesFirstLineRules() const { return metadata_.uses_first_line_rules; }
bool UsesWindowInactiveSelector() const { bool UsesWindowInactiveSelector() const {
return metadata_.uses_window_inactive_selector; return metadata_.uses_window_inactive_selector;
......
# CSS Style Invalidation in Blink
[Rendered](https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/core/css/style-invalidation.md)
# About this document
The doc gives a high-level overview of the style invalidation process.
It does not cover [sibling invalidation](https://goo.gl/z0Z9gn).
# Overview of invalidation
Invalidation is the process
of marking which elements need their style recalculated
in response to a change in the DOM.
The simplest possible approach
is to invalidate everything in response to every change.
Invalidation sets give us a way
to find a smaller set of elements which need recalculation
They are not perfect,
they err on the side of correctness,
so we invalidate elements that do not need recalculation
but this are significantly better than recalculating everything.
An invalidation set represents
* criteria for matching against a node
* instructions for whether/how to descend the tree into the node's children
If we have a style rule `".c1 .c2 { ... }"`
then style recalculation is needed
when a `c1`-class is added or removed
as an ancestor of a `c2`-class
or when a `c2`-class is added or removed
as a descendant of a `c1`-class element
(here adding/removing can be adding/removing an element
or just adding/removing these classes on existing elements).
We don't want to do full style recalc
at the time of adding/removing
because there may be more mutations coming.
If we can tell immediately that a change forces style recalc
then we mark the node as that,
otherwise we collect everything as pending invalidation sets.
In the example,
if a `c1`-class is added to an element in the tree,
we need to invalidate all of its descendants which have class c2.
Rather than perform a search right now,
we just mark the element with a pending invalidation set
that matches against `c2`
and descends into all light-descendants.
If a `c2`-class element is added to the tree,
then it needs recalculation
if it has a `c1`-class ancestor.
We never search _up_ the tree at this point
or during style invalidation,
we only do that during recalculation,
so this becomes an immediate invalidation,
even though it may be unnecessary.
Eventually all DOM changes have been turned into immediate invalidations
or pending invalidation sets.
At this point,
we apply all the pending invalidations
and then recalculate style for all of the invalidated elements.
For more details see the original [invalidation sets design doc](https://goo.gl/3ane6s)
and the [sibling invalidation design doc](https://goo.gl/z0Z9gn).
# State involved
## RuleFeatureSet
The data in RuleFeatureSet is built from the style rules
and does not change unless the style rules change.
## DOM Node's style states
DOM nodes have several bits of style-related state
that control style invalidation and recalculation.
These are accessed through:
* [`Node::NeedsStyleInvalidation`](https://cs.chromium.org/?q=symbol:%5Eblink::Node::NeedsStyleInvalidation$)
* [`Node::ChildNeedsStyleInvalidation`](https://cs.chromium.org/?q=symbol:%5Eblink::Node::ChildNeedsStyleInvalidation$)
* [`Node::NeedsStyleRecalc`](https://cs.chromium.org/?q=symbol:%5Eblink::Node::NeedsStyleRecalc$)
* [`Node::ChildNeedsStyleRecalc`](https://cs.chromium.org/?q=symbol:%5Eblink::Node::ChildNeedsStyleRecalc$)
## PendingInvalidationsMap
This is a map from
DOM Node to
invalidation sets that should be applied due to updates to that node.
# Processes
## Overview
The overview of style invalidation and recalculation is that
* Style rules are compiled down to a collection of InvalidationSets
and other data
in [`RuleFeatureSet`](https://cs.chromium.org/?q=symbol:%5Eblink::RuleFeatureSet$)
* The following process is then applied continuously
* Changes to the DOM cause nodes or subtrees to be immediately invalidated
or to accumulate pending invalidations.
* A point is reached where style is being read
(e.g. in order to render a new frame)
* Style invalidation process finds all pending invalidations
and decides on what will actually be recalculated
* Style recalculation process recalculates style
for all nodes that need it
## Building the RuleFeatureSet
Each [`RuleSet`](https://cs.chromium.org/?q=symbol:%5Eblink::RuleSet$)
produces its own
[`RuleFeatureSet`](https://cs.chromium.org/?q=symbol:%5Eblink::RuleFeatureSet$)
by calling [`CollectFeaturesFromRuleData`](https://cs.chromium.org/?q=symbol:%5Eblink::RuleFeatureSet::CollectFeaturesFromRuleData$)
for each [`RuleData`](https://cs.chromium.org/?q=symbol:%5Eblink::RuleData$).
These contain several indexed collections of [`InvalidationSet`](https://cs.chromium.org/?q=symbol:%5Eblink::InvalidationSet$)s
and some miscellaneous properties.
All of these are merged together to form a final [`RuleFeatureSet`](https://cs.chromium.org/?q=symbol:%5Eblink::RuleFeatureSet$)
which used for style purposes.
## Turning DOM changes into pending/immediate invalidations
Changes in the DOM that require updates to styles
may get turned into either immediate invalidations or pending invalidations.
When a DOM change that could impact style occurs inside a [`Node`](https://cs.chromium.org/?q=symbol:%5Eblink::Node$)
(e.g. a change in class name)
this leads to a call into [`StyleEngine`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleEngine$)
to record this style-impacting change via one of several [`FooChangedForElement`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleEngine::.*ChangedForElement$) methods.
Depending on the type of change,
[`StyleEngine`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleEngine$) gathers the relevant [`InvalidationSet`](https://cs.chromium.org/?q=symbol:%5Eblink::InvalidationSet$)s
and calls [`PendingInvalidations::ScheduleInvalidationSetsForNode`](https://cs.chromium.org/?q=symbol:%5Eblink::PendingInvalidations::ScheduleInvalidationSetsForNode$)
which will do one or both of
* call [`Node::SetNeedsStyleInvalidation`](https://cs.chromium.org/?q=symbol:%5Eblink::Node::SetNeedsStyleInvalidation$)
which ensures that the invalidation process will consider this node
and add InvalidationSets for this node to the pending invalidation set map.
* call [`Node::SetNeedsStyleRecalc`](https://cs.chromium.org/?q=symbol:%5Eblink::Node::SetNeedsStyleRecalc$)
with either [`kLocalStyleChange`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleChangeType::kLocalStyleChange$) or [`kSubtreeStyleChange`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleChangeType::kSubtreeStyleChange$)
## Pushing the pending invalidations
When style is about to be read,
the map of pending invalidations which has been built up
needs to be pushed.
For each [`ContainerNode`](https://cs.chromium.org/?q=symbol:%5Eblink::ContainerNode$) in the DOM tree
we have 0 or more descendant [`InvalidationSet`](https://cs.chromium.org/?q=symbol:%5Eblink::InvalidationSet$) waiting to be applied.
The invalidation process starts with a call to [`StyleInvalidator::Invalidate`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleInvalidator::Invalidate$)
which recurses down the tree, depth first.
Read the method's inline documentation to understand more about the process.
# See Also
[Invalidation sets design doc](https://goo.gl/3ane6s)
[Sibling invalidation design doc](https://goo.gl/z0Z9gn)
...@@ -76,10 +76,17 @@ class WebPluginContainerImpl; ...@@ -76,10 +76,17 @@ class WebPluginContainerImpl;
const int kNodeStyleChangeShift = 18; const int kNodeStyleChangeShift = 18;
const int kNodeCustomElementShift = 20; const int kNodeCustomElementShift = 20;
// Values for kChildNeedsStyleRecalcFlag, controlling whether a node gets its
// style recalculated.
enum StyleChangeType { enum StyleChangeType {
// This node does not need style recalculation.
kNoStyleChange = 0, kNoStyleChange = 0,
// This node needs style recalculation.
kLocalStyleChange = 1 << kNodeStyleChangeShift, kLocalStyleChange = 1 << kNodeStyleChangeShift,
// This node and all of its flat-tree descendeants need style recalculation.
kSubtreeStyleChange = 2 << kNodeStyleChangeShift, kSubtreeStyleChange = 2 << kNodeStyleChangeShift,
// This node and all of its descendants are detached and need style
// recalculation.
kNeedsReattachStyleChange = 3 << kNodeStyleChangeShift, kNeedsReattachStyleChange = 3 << kNodeStyleChangeShift,
}; };
...@@ -401,6 +408,7 @@ class CORE_EXPORT Node : public EventTarget { ...@@ -401,6 +408,7 @@ class CORE_EXPORT Node : public EventTarget {
bool NeedsAttach() const { bool NeedsAttach() const {
return GetStyleChangeType() == kNeedsReattachStyleChange; return GetStyleChangeType() == kNeedsReattachStyleChange;
} }
// True if the style recalc process should recalculate style for this node.
bool NeedsStyleRecalc() const { bool NeedsStyleRecalc() const {
// We do not ClearNeedsStyleRecalc() if the recalc triggers a layout re- // We do not ClearNeedsStyleRecalc() if the recalc triggers a layout re-
// attachment (see Element::RecalcStyle()). In order to avoid doing an extra // attachment (see Element::RecalcStyle()). In order to avoid doing an extra
...@@ -412,6 +420,8 @@ class CORE_EXPORT Node : public EventTarget { ...@@ -412,6 +420,8 @@ class CORE_EXPORT Node : public EventTarget {
StyleChangeType GetStyleChangeType() const { StyleChangeType GetStyleChangeType() const {
return static_cast<StyleChangeType>(node_flags_ & kStyleChangeMask); return static_cast<StyleChangeType>(node_flags_ & kStyleChangeMask);
} }
// True if the style recalculation process should traverse this node's
// children when looking for nodes that need recalculation.
bool ChildNeedsStyleRecalc() const { bool ChildNeedsStyleRecalc() const {
return GetFlag(kChildNeedsStyleRecalcFlag); return GetFlag(kChildNeedsStyleRecalcFlag);
} }
...@@ -428,6 +438,8 @@ class CORE_EXPORT Node : public EventTarget { ...@@ -428,6 +438,8 @@ class CORE_EXPORT Node : public EventTarget {
void SetChildNeedsStyleRecalc() { SetFlag(kChildNeedsStyleRecalcFlag); } void SetChildNeedsStyleRecalc() { SetFlag(kChildNeedsStyleRecalcFlag); }
void ClearChildNeedsStyleRecalc() { ClearFlag(kChildNeedsStyleRecalcFlag); } void ClearChildNeedsStyleRecalc() { ClearFlag(kChildNeedsStyleRecalcFlag); }
// Sets the flag for the current node and also calls
// MarkAncestorsWithChildNeedsStyleRecalc
void SetNeedsStyleRecalc(StyleChangeType, const StyleChangeReasonForTracing&); void SetNeedsStyleRecalc(StyleChangeType, const StyleChangeReasonForTracing&);
void ClearNeedsStyleRecalc(); void ClearNeedsStyleRecalc();
...@@ -463,6 +475,8 @@ class CORE_EXPORT Node : public EventTarget { ...@@ -463,6 +475,8 @@ class CORE_EXPORT Node : public EventTarget {
} }
void MarkAncestorsWithChildNeedsDistributionRecalc(); void MarkAncestorsWithChildNeedsDistributionRecalc();
// True if the style invalidation process should traverse this node's children
// when looking for pending invalidations.
bool ChildNeedsStyleInvalidation() const { bool ChildNeedsStyleInvalidation() const {
return GetFlag(kChildNeedsStyleInvalidationFlag); return GetFlag(kChildNeedsStyleInvalidationFlag);
} }
...@@ -473,10 +487,14 @@ class CORE_EXPORT Node : public EventTarget { ...@@ -473,10 +487,14 @@ class CORE_EXPORT Node : public EventTarget {
ClearFlag(kChildNeedsStyleInvalidationFlag); ClearFlag(kChildNeedsStyleInvalidationFlag);
} }
void MarkAncestorsWithChildNeedsStyleInvalidation(); void MarkAncestorsWithChildNeedsStyleInvalidation();
// True if there are pending invalidations against this node.
bool NeedsStyleInvalidation() const { bool NeedsStyleInvalidation() const {
return GetFlag(kNeedsStyleInvalidationFlag); return GetFlag(kNeedsStyleInvalidationFlag);
} }
void ClearNeedsStyleInvalidation() { ClearFlag(kNeedsStyleInvalidationFlag); } void ClearNeedsStyleInvalidation() { ClearFlag(kNeedsStyleInvalidationFlag); }
// Sets the flag for the current node and also calls
// MarkAncestorsWithChildNeedsStyleInvalidation
void SetNeedsStyleInvalidation(); void SetNeedsStyleInvalidation();
// This needs to be called before using FlatTreeTraversal. // This needs to be called before using FlatTreeTraversal.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment