1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* the interface (to internal code) for retrieving computed style data */
9 #include "mozilla/ComputedStyle.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/Maybe.h"
12 #include "mozilla/ToString.h"
14 #include "nsCSSAnonBoxes.h"
15 #include "nsCSSPseudoElements.h"
16 #include "nsFontMetrics.h"
17 #include "nsStyleConsts.h"
18 #include "nsStyleStruct.h"
19 #include "nsStyleStructInlines.h"
21 #include "nsPresContext.h"
22 #include "nsWindowSizes.h"
26 #include "mozilla/dom/Document.h"
27 #include "nsPrintfCString.h"
28 #include "RubyUtils.h"
29 #include "mozilla/ComputedStyleInlines.h"
30 #include "mozilla/Preferences.h"
31 #include "mozilla/ProfilerLabels.h"
33 #include "mozilla/ReflowInput.h"
34 #include "nsLayoutUtils.h"
37 // Ensure the binding function declarations in ComputedStyle.h matches
38 // those in ServoBindings.h.
39 #include "mozilla/ServoBindings.h"
43 ComputedStyle::ComputedStyle(PseudoStyleType aPseudoType
,
44 ServoComputedDataForgotten aComputedValues
)
45 : mSource(aComputedValues
), mPseudoType(aPseudoType
) {}
47 // If a struct returned nsChangeHint_UpdateContainingBlock, that means that one
48 // property's influence on whether we're a containing block for abs-pos or
49 // fixed-pos elements has changed.
51 // However, we only need to return the hint if the overall computation of
52 // whether we establish a containing block has really changed.
53 static bool ContainingBlockMayHaveChanged(const ComputedStyle
& aOldStyle
,
54 const ComputedStyle
& aNewStyle
) {
55 auto* oldDisp
= aOldStyle
.StyleDisplay();
56 auto* newDisp
= aNewStyle
.StyleDisplay();
58 if (oldDisp
->IsPositionedStyle() != newDisp
->IsPositionedStyle()) {
63 oldDisp
->IsFixedPosContainingBlockForNonSVGTextFrames(aOldStyle
);
65 newDisp
->IsFixedPosContainingBlockForNonSVGTextFrames(aNewStyle
)) {
68 // If we were both before and after a fixed-pos containing-block that means
69 // that everything else doesn't matter, since all the other conditions are a
74 // Note that neither of these two following sets of frames
75 // (transform-supporting and layout-and-paint-supporting frames) is a subset
76 // of the other, because table frames support contain: layout/paint but not
77 // transforms (which are instead inherited to the table wrapper), and quite a
78 // few frame types support transforms but not contain: layout/paint (e.g.,
79 // table rows and row groups, many SVG frames).
80 if (oldDisp
->IsFixedPosContainingBlockForTransformSupportingFrames() !=
81 newDisp
->IsFixedPosContainingBlockForTransformSupportingFrames()) {
85 ->IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames() !=
87 ->IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames()) {
93 nsChangeHint
ComputedStyle::CalcStyleDifference(const ComputedStyle
& aNewStyle
,
94 uint32_t* aEqualStructs
) const {
95 AUTO_PROFILER_LABEL("ComputedStyle::CalcStyleDifference", LAYOUT
);
96 static_assert(StyleStructConstants::kStyleStructCount
<= 32,
97 "aEqualStructs is not big enough");
101 nsChangeHint hint
= nsChangeHint(0);
102 // We must always ensure that we populate the structs on the new style
103 // context that are filled in on the old context, so that if we get
104 // two style changes in succession, the second of which causes a real
105 // style change, the PeekStyleData doesn't return null (implying that
106 // nobody ever looked at that struct's data). In other words, we
107 // can't skip later structs if we get a big change up front, because
108 // we could later get a small change in one of those structs that we
109 // don't want to miss.
111 DebugOnly
<uint32_t> structsFound
= 0;
113 DebugOnly
<int> styleStructCount
= 0;
115 // Servo's optimization to stop the cascade when there are no style changes
116 // that children need to be recascade for relies on comparing all of the
117 // structs, not just those that are returned from PeekStyleData, although
118 // if PeekStyleData does return null we could avoid to accumulate any change
119 // hints for those structs.
121 // FIXME(emilio): Reintroduce that optimization either for all kind of structs
122 // after bug 1368290 with a weak parent pointer from text, or just for reset
124 #define STYLE_STRUCT_BIT(name_) \
125 StyleStructConstants::BitFor(StyleStructID::name_)
127 #define EXPAND(...) __VA_ARGS__
128 #define DO_STRUCT_DIFFERENCE_WITH_ARGS(struct_, extra_args_) \
130 const nsStyle##struct_* this##struct_ = Style##struct_(); \
131 structsFound |= STYLE_STRUCT_BIT(struct_); \
133 const nsStyle##struct_* other##struct_ = aNewStyle.Style##struct_(); \
134 if (this##struct_ == other##struct_) { \
135 /* The very same struct, so we know that there will be no */ \
137 *aEqualStructs |= STYLE_STRUCT_BIT(struct_); \
139 nsChangeHint difference = \
140 this##struct_->CalcDifference(*other##struct_ EXPAND extra_args_); \
141 hint |= difference; \
143 *aEqualStructs |= STYLE_STRUCT_BIT(struct_); \
146 styleStructCount++; \
148 #define DO_STRUCT_DIFFERENCE(struct_) \
149 DO_STRUCT_DIFFERENCE_WITH_ARGS(struct_, ())
151 // FIXME: The order of these DO_STRUCT_DIFFERENCE calls is no longer
152 // significant. With a small amount of effort, we could replace them with a
153 // #include "nsStyleStructList.h".
154 DO_STRUCT_DIFFERENCE_WITH_ARGS(Display
, (, *StylePosition()));
155 DO_STRUCT_DIFFERENCE(XUL
);
156 DO_STRUCT_DIFFERENCE(Column
);
157 DO_STRUCT_DIFFERENCE(Content
);
158 DO_STRUCT_DIFFERENCE(UI
);
159 DO_STRUCT_DIFFERENCE(Visibility
);
160 DO_STRUCT_DIFFERENCE(Outline
);
161 DO_STRUCT_DIFFERENCE(TableBorder
);
162 DO_STRUCT_DIFFERENCE(Table
);
163 DO_STRUCT_DIFFERENCE(UIReset
);
164 DO_STRUCT_DIFFERENCE(Text
);
165 DO_STRUCT_DIFFERENCE_WITH_ARGS(List
, (, *StyleDisplay()));
166 DO_STRUCT_DIFFERENCE(SVGReset
);
167 DO_STRUCT_DIFFERENCE(SVG
);
168 DO_STRUCT_DIFFERENCE_WITH_ARGS(Position
, (, *StyleVisibility()));
169 DO_STRUCT_DIFFERENCE(Font
);
170 DO_STRUCT_DIFFERENCE(Margin
);
171 DO_STRUCT_DIFFERENCE(Padding
);
172 DO_STRUCT_DIFFERENCE(Border
);
173 DO_STRUCT_DIFFERENCE(TextReset
);
174 DO_STRUCT_DIFFERENCE(Effects
);
175 DO_STRUCT_DIFFERENCE(Background
);
176 DO_STRUCT_DIFFERENCE(Page
);
178 #undef DO_STRUCT_DIFFERENCE
179 #undef DO_STRUCT_DIFFERENCE_WITH_ARGS
182 MOZ_ASSERT(styleStructCount
== StyleStructConstants::kStyleStructCount
,
183 "missing a call to DO_STRUCT_DIFFERENCE");
185 // Note that we do not check whether this->RelevantLinkVisited() !=
186 // aNewContext->RelevantLinkVisited(); we don't need to since
187 // nsCSSFrameConstructor::DoContentStateChanged always adds
188 // nsChangeHint_RepaintFrame for NS_EVENT_STATE_VISITED changes (and
189 // needs to, since HasStateDependentStyle probably doesn't work right
190 // for NS_EVENT_STATE_VISITED). Hopefully this doesn't actually
191 // expose whether links are visited to performance tests since all
192 // link coloring happens asynchronously at a time when it's hard for
193 // the page to measure.
194 // However, we do need to compute the larger of the changes that can
195 // happen depending on whether the link is visited or unvisited, since
196 // doing only the one that's currently appropriate would expose which
197 // links are in history to easy performance measurement. Therefore,
198 // here, we add nsChangeHint_RepaintFrame hints (the maximum for
199 // things that can depend on :visited) for the properties on which we
200 // call GetVisitedDependentColor.
201 const ComputedStyle
* thisVis
= GetStyleIfVisited();
202 const ComputedStyle
* otherVis
= aNewStyle
.GetStyleIfVisited();
203 if (!thisVis
!= !otherVis
) {
204 // One style has a style-if-visited and the other doesn't.
205 // Presume a difference.
206 #define STYLE_STRUCT(name_, fields_) *aEqualStructs &= ~STYLE_STRUCT_BIT(name_);
207 #include "nsCSSVisitedDependentPropList.h"
209 hint
|= nsChangeHint_RepaintFrame
;
210 } else if (thisVis
) {
211 // Both styles have a style-if-visited.
214 // NB: Calling Peek on |this|, not |thisVis|, since callers may look
215 // at a struct on |this| without looking at the same struct on
216 // |thisVis| (including this function if we skip one of these checks
217 // due to change being true already or due to the old style not having a
218 // style-if-visited), but not the other way around.
219 #define STYLE_FIELD(name_) thisVisStruct->name_ != otherVisStruct->name_
220 #define STYLE_STRUCT(name_, fields_) \
222 const nsStyle##name_* thisVisStruct = thisVis->Style##name_(); \
223 const nsStyle##name_* otherVisStruct = otherVis->Style##name_(); \
224 if (MOZ_FOR_EACH_SEPARATED(STYLE_FIELD, (||), (), fields_)) { \
225 *aEqualStructs &= ~STYLE_STRUCT_BIT(name_); \
229 #include "nsCSSVisitedDependentPropList.h"
232 #undef STYLE_STRUCT_BIT
235 hint
|= nsChangeHint_RepaintFrame
;
239 if (hint
& nsChangeHint_UpdateContainingBlock
) {
240 if (!ContainingBlockMayHaveChanged(*this, aNewStyle
)) {
241 // While some styles that cause the frame to be a containing block
242 // has changed, the overall result cannot have changed (no matter
243 // what the frame type is).
244 hint
&= ~nsChangeHint_UpdateContainingBlock
;
248 if (HasAuthorSpecifiedBorderOrBackground() !=
249 aNewStyle
.HasAuthorSpecifiedBorderOrBackground()) {
250 const StyleAppearance appearance
= StyleDisplay()->EffectiveAppearance();
251 if (appearance
!= StyleAppearance::None
&&
252 nsLayoutUtils::AuthorSpecifiedBorderBackgroundDisablesTheming(
254 // A background-specified change may cause padding to change, so we may
255 // need to reflow. We use the same hint here as we do for "appearance"
257 hint
|= nsChangeHint_AllReflowHints
| nsChangeHint_RepaintFrame
;
261 MOZ_ASSERT(NS_IsHintSubset(hint
, nsChangeHint_AllHints
),
262 "Added a new hint without bumping AllHints?");
263 return hint
& ~nsChangeHint_NeutralChange
;
267 void ComputedStyle::List(FILE* out
, int32_t aIndent
) {
271 for (ix
= aIndent
; --ix
>= 0;) {
272 str
.AppendLiteral(" ");
274 str
.Append(nsPrintfCString("%p(%d) parent=%p ", (void*)this, 0, nullptr));
275 if (mPseudoType
!= PseudoStyleType::NotPseudo
) {
276 str
.Append(nsPrintfCString("%s ", ToString(mPseudoType
).c_str()));
279 fprintf_stderr(out
, "%s{ServoComputedData}\n", str
.get());
283 template <typename Func
>
284 static nscolor
GetVisitedDependentColorInternal(const ComputedStyle
& aStyle
,
287 colors
[0] = aColorFunc(aStyle
);
288 if (const ComputedStyle
* visitedStyle
= aStyle
.GetStyleIfVisited()) {
289 colors
[1] = aColorFunc(*visitedStyle
);
290 return ComputedStyle::CombineVisitedColors(colors
,
291 aStyle
.RelevantLinkVisited());
296 static nscolor
ExtractColor(const ComputedStyle
& aStyle
,
297 const StyleRGBA
& aColor
) {
298 return aColor
.ToColor();
301 static nscolor
ExtractColor(const ComputedStyle
& aStyle
,
302 const StyleColor
& aColor
) {
303 return aColor
.CalcColor(aStyle
);
306 // Currently caret-color, the only property in the list which is a ColorOrAuto,
307 // always maps auto to currentcolor.
308 static nscolor
ExtractColor(const ComputedStyle
& aStyle
,
309 const StyleColorOrAuto
& aColor
) {
310 if (aColor
.IsAuto()) {
311 return ExtractColor(aStyle
, StyleColor::CurrentColor());
313 return ExtractColor(aStyle
, aColor
.AsColor());
316 static nscolor
ExtractColor(const ComputedStyle
& aStyle
,
317 const StyleSVGPaint
& aPaintServer
) {
318 return aPaintServer
.kind
.IsColor()
319 ? ExtractColor(aStyle
, aPaintServer
.kind
.AsColor())
320 : NS_RGBA(0, 0, 0, 0);
323 #define STYLE_FIELD(struct_, field_) aField == &struct_::field_ ||
324 #define STYLE_STRUCT(name_, fields_) \
326 nscolor ComputedStyle::GetVisitedDependentColor(decltype( \
327 nsStyle##name_::MOZ_ARG_1 fields_) nsStyle##name_::*aField) const { \
328 MOZ_ASSERT(MOZ_FOR_EACH(STYLE_FIELD, (nsStyle##name_, ), fields_) false, \
329 "Getting visited-dependent color for a field in nsStyle" #name_ \
330 " which is not listed in nsCSSVisitedDependentPropList.h"); \
331 return GetVisitedDependentColorInternal( \
332 *this, [aField](const ComputedStyle& aStyle) { \
333 return ExtractColor(aStyle, aStyle.Style##name_()->*aField); \
336 #include "nsCSSVisitedDependentPropList.h"
340 struct ColorIndexSet
{
341 uint8_t colorIndex
, alphaIndex
;
344 static const ColorIndexSet gVisitedIndices
[2] = {{0, 0}, {1, 0}};
347 nscolor
ComputedStyle::CombineVisitedColors(nscolor
* aColors
,
348 bool aLinkIsVisited
) {
349 if (NS_GET_A(aColors
[1]) == 0) {
350 // If the style-if-visited is transparent, then just use the
351 // unvisited style rather than using the (meaningless) color
352 // components of the visited style along with a potentially
353 // non-transparent alpha value.
354 aLinkIsVisited
= false;
357 // NOTE: We want this code to have as little timing dependence as
358 // possible on whether this->RelevantLinkVisited() is true.
359 const ColorIndexSet
& set
= gVisitedIndices
[aLinkIsVisited
? 1 : 0];
361 nscolor colorColor
= aColors
[set
.colorIndex
];
362 nscolor alphaColor
= aColors
[set
.alphaIndex
];
363 return NS_RGBA(NS_GET_R(colorColor
), NS_GET_G(colorColor
),
364 NS_GET_B(colorColor
), NS_GET_A(alphaColor
));
368 /* static */ const char* ComputedStyle::StructName(StyleStructID aSID
) {
370 # define STYLE_STRUCT(name_) \
371 case StyleStructID::name_: \
373 # include "nsStyleStructList.h"
381 Maybe
<StyleStructID
> ComputedStyle::LookupStruct(const nsACString
& aName
) {
382 # define STYLE_STRUCT(name_) \
383 if (aName.EqualsLiteral(#name_)) return Some(StyleStructID::name_);
384 # include "nsStyleStructList.h"
390 ComputedStyle
* ComputedStyle::GetCachedLazyPseudoStyle(
391 PseudoStyleType aPseudo
) const {
392 MOZ_ASSERT(PseudoStyle::IsPseudoElement(aPseudo
));
394 if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(aPseudo
)) {
398 return mCachedInheritingStyles
.Lookup(aPseudo
);
401 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoComputedValuesMallocEnclosingSizeOf
)
403 void ComputedStyle::AddSizeOfIncludingThis(nsWindowSizes
& aSizes
,
404 size_t* aCVsSize
) const {
405 // Note: |this| sits within a servo_arc::Arc, i.e. it is preceded by a
406 // refcount. So we need to measure it with a function that can handle an
407 // interior pointer. We use ServoComputedValuesMallocEnclosingSizeOf to
408 // clearly identify in DMD's output the memory measured here.
409 *aCVsSize
+= ServoComputedValuesMallocEnclosingSizeOf(this);
410 mSource
.AddSizeOfExcludingThis(aSizes
);
411 mCachedInheritingStyles
.AddSizeOfIncludingThis(aSizes
, aCVsSize
);
415 bool ComputedStyle::EqualForCachedAnonymousContentStyle(
416 const ComputedStyle
& aOther
) const {
417 // One thing we can't add UA rules to prevent is different -x-lang
418 // values being inherited in. So we use this FFI function function rather
419 // than rely on CalcStyleDifference, which can't tell us which specific
420 // properties have changed.
421 return Servo_ComputedValues_EqualForCachedAnonymousContentStyle(this,
427 } // namespace mozilla