no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / layout / style / ComputedStyle.cpp
blob1cb291b27bf8d1d740ff9a0d9a273d036d957035
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"
20 #include "nsString.h"
21 #include "nsPresContext.h"
22 #include "nsWindowSizes.h"
24 #include "nsCOMPtr.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"
35 #include "nsCoord.h"
37 // Ensure the binding function declarations in ComputedStyle.h matches
38 // those in ServoBindings.h.
39 #include "mozilla/ServoBindings.h"
41 namespace mozilla {
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 const auto& oldDisp = *aOldStyle.StyleDisplay();
56 const auto& newDisp = *aNewStyle.StyleDisplay();
58 if (oldDisp.IsPositionedStyle() != newDisp.IsPositionedStyle()) {
59 // XXX This check can probably be moved to after the fixedCB check, since
60 // IsPositionedStyle() is also only relevant for non-svg text frame
61 // subtrees.
62 return true;
65 const bool fixedCB = aOldStyle.IsFixedPosContainingBlockForNonSVGTextFrames();
66 if (fixedCB != aNewStyle.IsFixedPosContainingBlockForNonSVGTextFrames()) {
67 return true;
69 // If we were both before and after a fixed-pos containing-block that means
70 // that everything else doesn't matter, since all the other conditions are a
71 // subset of this.
72 if (fixedCB) {
73 return false;
76 // Note that neither of these two following sets of frames
77 // (transform-supporting and layout-and-paint-supporting frames) is a subset
78 // of the other, because table frames support contain: layout/paint but not
79 // transforms (which are instead inherited to the table wrapper), and quite a
80 // few frame types support transforms but not contain: layout/paint (e.g.,
81 // table rows and row groups, many SVG frames).
82 if (oldDisp.IsFixedPosContainingBlockForTransformSupportingFrames() !=
83 newDisp.IsFixedPosContainingBlockForTransformSupportingFrames()) {
84 return true;
86 if (oldDisp
87 .IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames() !=
88 newDisp
89 .IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames()) {
90 return true;
92 return false;
95 nsChangeHint ComputedStyle::CalcStyleDifference(const ComputedStyle& aNewStyle,
96 uint32_t* aEqualStructs) const {
97 AUTO_PROFILER_LABEL_HOT("ComputedStyle::CalcStyleDifference", LAYOUT);
98 static_assert(StyleStructConstants::kStyleStructCount <= 32,
99 "aEqualStructs is not big enough");
101 *aEqualStructs = 0;
103 nsChangeHint hint = nsChangeHint(0);
104 // We must always ensure that we populate the structs on the new style
105 // context that are filled in on the old context, so that if we get
106 // two style changes in succession, the second of which causes a real
107 // style change, the PeekStyleData doesn't return null (implying that
108 // nobody ever looked at that struct's data). In other words, we
109 // can't skip later structs if we get a big change up front, because
110 // we could later get a small change in one of those structs that we
111 // don't want to miss.
113 DebugOnly<uint32_t> structsFound = 0;
115 DebugOnly<int> styleStructCount = 0;
117 // Servo's optimization to stop the cascade when there are no style changes
118 // that children need to be recascade for relies on comparing all of the
119 // structs, not just those that are returned from PeekStyleData, although
120 // if PeekStyleData does return null we could avoid to accumulate any change
121 // hints for those structs.
123 // FIXME(emilio): Reintroduce that optimization either for all kind of structs
124 // after bug 1368290 with a weak parent pointer from text, or just for reset
125 // structs.
126 #define STYLE_STRUCT_BIT(name_) \
127 StyleStructConstants::BitFor(StyleStructID::name_)
129 #define EXPAND(...) __VA_ARGS__
130 #define DO_STRUCT_DIFFERENCE_WITH_ARGS(struct_, extra_args_) \
131 PR_BEGIN_MACRO \
132 const nsStyle##struct_* this##struct_ = Style##struct_(); \
133 structsFound |= STYLE_STRUCT_BIT(struct_); \
135 const nsStyle##struct_* other##struct_ = aNewStyle.Style##struct_(); \
136 if (this##struct_ == other##struct_) { \
137 /* The very same struct, so we know that there will be no */ \
138 /* differences. */ \
139 *aEqualStructs |= STYLE_STRUCT_BIT(struct_); \
140 } else { \
141 nsChangeHint difference = \
142 this##struct_->CalcDifference(*other##struct_ EXPAND extra_args_); \
143 hint |= difference; \
144 if (!difference) { \
145 *aEqualStructs |= STYLE_STRUCT_BIT(struct_); \
148 styleStructCount++; \
149 PR_END_MACRO
150 #define DO_STRUCT_DIFFERENCE(struct_) \
151 DO_STRUCT_DIFFERENCE_WITH_ARGS(struct_, ())
153 // FIXME: The order of these DO_STRUCT_DIFFERENCE calls is no longer
154 // significant. With a small amount of effort, we could replace them with a
155 // #include "nsStyleStructList.h".
156 DO_STRUCT_DIFFERENCE_WITH_ARGS(Display, (, *this));
157 DO_STRUCT_DIFFERENCE(XUL);
158 DO_STRUCT_DIFFERENCE(Column);
159 DO_STRUCT_DIFFERENCE(Content);
160 DO_STRUCT_DIFFERENCE(UI);
161 DO_STRUCT_DIFFERENCE(Visibility);
162 DO_STRUCT_DIFFERENCE(Outline);
163 DO_STRUCT_DIFFERENCE(TableBorder);
164 DO_STRUCT_DIFFERENCE(Table);
165 DO_STRUCT_DIFFERENCE(UIReset);
166 DO_STRUCT_DIFFERENCE(Text);
167 DO_STRUCT_DIFFERENCE_WITH_ARGS(List, (, *this));
168 DO_STRUCT_DIFFERENCE(SVGReset);
169 DO_STRUCT_DIFFERENCE(SVG);
170 DO_STRUCT_DIFFERENCE_WITH_ARGS(Position, (, *this));
171 DO_STRUCT_DIFFERENCE(Font);
172 DO_STRUCT_DIFFERENCE(Margin);
173 DO_STRUCT_DIFFERENCE(Padding);
174 DO_STRUCT_DIFFERENCE(Border);
175 DO_STRUCT_DIFFERENCE(TextReset);
176 DO_STRUCT_DIFFERENCE(Effects);
177 DO_STRUCT_DIFFERENCE(Background);
178 DO_STRUCT_DIFFERENCE(Page);
180 #undef DO_STRUCT_DIFFERENCE
181 #undef DO_STRUCT_DIFFERENCE_WITH_ARGS
182 #undef EXPAND
184 MOZ_ASSERT(styleStructCount == StyleStructConstants::kStyleStructCount,
185 "missing a call to DO_STRUCT_DIFFERENCE");
187 // Note that we do not check whether this->RelevantLinkVisited() !=
188 // aNewContext->RelevantLinkVisited(); we don't need to since
189 // nsCSSFrameConstructor::DoContentStateChanged always adds
190 // nsChangeHint_RepaintFrame for ElementState::VISITED changes (and
191 // needs to, since HasStateDependentStyle probably doesn't work right
192 // for ElementState::VISITED). Hopefully this doesn't actually
193 // expose whether links are visited to performance tests since all
194 // link coloring happens asynchronously at a time when it's hard for
195 // the page to measure.
196 // However, we do need to compute the larger of the changes that can
197 // happen depending on whether the link is visited or unvisited, since
198 // doing only the one that's currently appropriate would expose which
199 // links are in history to easy performance measurement. Therefore,
200 // here, we add nsChangeHint_RepaintFrame hints (the maximum for
201 // things that can depend on :visited) for the properties on which we
202 // call GetVisitedDependentColor.
203 const ComputedStyle* thisVis = GetStyleIfVisited();
204 const ComputedStyle* otherVis = aNewStyle.GetStyleIfVisited();
205 if (!thisVis != !otherVis) {
206 // One style has a style-if-visited and the other doesn't.
207 // Presume a difference.
208 #define STYLE_STRUCT(name_, fields_) *aEqualStructs &= ~STYLE_STRUCT_BIT(name_);
209 #include "nsCSSVisitedDependentPropList.h"
210 #undef STYLE_STRUCT
211 hint |= nsChangeHint_RepaintFrame;
212 } else if (thisVis) {
213 // Both styles have a style-if-visited.
214 bool change = false;
216 // NB: Calling Peek on |this|, not |thisVis|, since callers may look
217 // at a struct on |this| without looking at the same struct on
218 // |thisVis| (including this function if we skip one of these checks
219 // due to change being true already or due to the old style not having a
220 // style-if-visited), but not the other way around.
221 #define STYLE_FIELD(name_) thisVisStruct->name_ != otherVisStruct->name_
222 #define STYLE_STRUCT(name_, fields_) \
224 const nsStyle##name_* thisVisStruct = thisVis->Style##name_(); \
225 const nsStyle##name_* otherVisStruct = otherVis->Style##name_(); \
226 if (MOZ_FOR_EACH_SEPARATED(STYLE_FIELD, (||), (), fields_)) { \
227 *aEqualStructs &= ~STYLE_STRUCT_BIT(name_); \
228 change = true; \
231 #include "nsCSSVisitedDependentPropList.h"
232 #undef STYLE_STRUCT
233 #undef STYLE_FIELD
234 #undef STYLE_STRUCT_BIT
236 if (change) {
237 hint |= nsChangeHint_RepaintFrame;
241 if (hint & nsChangeHint_UpdateContainingBlock) {
242 if (!ContainingBlockMayHaveChanged(*this, aNewStyle)) {
243 // While some styles that cause the frame to be a containing block
244 // has changed, the overall result cannot have changed (no matter
245 // what the frame type is).
246 hint &= ~nsChangeHint_UpdateContainingBlock;
250 if (HasAuthorSpecifiedBorderOrBackground() !=
251 aNewStyle.HasAuthorSpecifiedBorderOrBackground()) {
252 const StyleAppearance appearance = StyleDisplay()->EffectiveAppearance();
253 if (appearance != StyleAppearance::None &&
254 nsLayoutUtils::AuthorSpecifiedBorderBackgroundDisablesTheming(
255 appearance)) {
256 // A background-specified change may cause padding to change, so we may
257 // need to reflow. We use the same hint here as we do for "appearance"
258 // changes.
259 hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
263 MOZ_ASSERT(NS_IsHintSubset(hint, nsChangeHint_AllHints),
264 "Added a new hint without bumping AllHints?");
265 return hint & ~nsChangeHint_NeutralChange;
268 #ifdef DEBUG
269 void ComputedStyle::List(FILE* out, int32_t aIndent) {
270 nsAutoCString str;
271 // Indent
272 int32_t ix;
273 for (ix = aIndent; --ix >= 0;) {
274 str.AppendLiteral(" ");
276 str.Append(nsPrintfCString("%p(%d) parent=%p ", (void*)this, 0, nullptr));
277 if (mPseudoType != PseudoStyleType::NotPseudo) {
278 str.Append(nsPrintfCString("%s ", ToString(mPseudoType).c_str()));
281 fprintf_stderr(out, "%s{ServoComputedData}\n", str.get());
283 #endif
285 template <typename Func>
286 static nscolor GetVisitedDependentColorInternal(const ComputedStyle& aStyle,
287 Func aColorFunc) {
288 nscolor colors[2];
289 colors[0] = aColorFunc(aStyle);
290 if (const ComputedStyle* visitedStyle = aStyle.GetStyleIfVisited()) {
291 colors[1] = aColorFunc(*visitedStyle);
292 return ComputedStyle::CombineVisitedColors(colors,
293 aStyle.RelevantLinkVisited());
295 return colors[0];
298 static nscolor ExtractColor(const ComputedStyle& aStyle,
299 const StyleAbsoluteColor& aColor) {
300 return aColor.ToColor();
303 static nscolor ExtractColor(const ComputedStyle& aStyle,
304 const StyleColor& aColor) {
305 return aColor.CalcColor(aStyle);
308 // Currently caret-color, the only property in the list which is a ColorOrAuto,
309 // always maps auto to currentcolor.
310 static nscolor ExtractColor(const ComputedStyle& aStyle,
311 const StyleColorOrAuto& aColor) {
312 if (aColor.IsAuto()) {
313 return ExtractColor(aStyle, StyleColor::CurrentColor());
315 return ExtractColor(aStyle, aColor.AsColor());
318 static nscolor ExtractColor(const ComputedStyle& aStyle,
319 const StyleSVGPaint& aPaintServer) {
320 return aPaintServer.kind.IsColor()
321 ? ExtractColor(aStyle, aPaintServer.kind.AsColor())
322 : NS_RGBA(0, 0, 0, 0);
325 #define STYLE_FIELD(struct_, field_) aField == &struct_::field_ ||
326 #define STYLE_STRUCT(name_, fields_) \
327 template <> \
328 nscolor ComputedStyle::GetVisitedDependentColor( \
329 decltype(nsStyle##name_::MOZ_ARG_1 fields_) nsStyle##name_::*aField) \
330 const { \
331 MOZ_ASSERT(MOZ_FOR_EACH(STYLE_FIELD, (nsStyle##name_, ), fields_) false, \
332 "Getting visited-dependent color for a field in nsStyle" #name_ \
333 " which is not listed in nsCSSVisitedDependentPropList.h"); \
334 return GetVisitedDependentColorInternal( \
335 *this, [aField](const ComputedStyle& aStyle) { \
336 return ExtractColor(aStyle, aStyle.Style##name_()->*aField); \
337 }); \
339 #include "nsCSSVisitedDependentPropList.h"
340 #undef STYLE_STRUCT
341 #undef STYLE_FIELD
343 struct ColorIndexSet {
344 uint8_t colorIndex, alphaIndex;
347 static const ColorIndexSet gVisitedIndices[2] = {{0, 0}, {1, 0}};
349 /* static */
350 nscolor ComputedStyle::CombineVisitedColors(nscolor* aColors,
351 bool aLinkIsVisited) {
352 if (NS_GET_A(aColors[1]) == 0) {
353 // If the style-if-visited is transparent, then just use the
354 // unvisited style rather than using the (meaningless) color
355 // components of the visited style along with a potentially
356 // non-transparent alpha value.
357 aLinkIsVisited = false;
360 // NOTE: We want this code to have as little timing dependence as
361 // possible on whether this->RelevantLinkVisited() is true.
362 const ColorIndexSet& set = gVisitedIndices[aLinkIsVisited ? 1 : 0];
364 nscolor colorColor = aColors[set.colorIndex];
365 nscolor alphaColor = aColors[set.alphaIndex];
366 return NS_RGBA(NS_GET_R(colorColor), NS_GET_G(colorColor),
367 NS_GET_B(colorColor), NS_GET_A(alphaColor));
370 #ifdef DEBUG
371 /* static */ const char* ComputedStyle::StructName(StyleStructID aSID) {
372 switch (aSID) {
373 # define STYLE_STRUCT(name_) \
374 case StyleStructID::name_: \
375 return #name_;
376 # include "nsStyleStructList.h"
377 # undef STYLE_STRUCT
378 default:
379 return "Unknown";
383 /* static */
384 Maybe<StyleStructID> ComputedStyle::LookupStruct(const nsACString& aName) {
385 # define STYLE_STRUCT(name_) \
386 if (aName.EqualsLiteral(#name_)) return Some(StyleStructID::name_);
387 # include "nsStyleStructList.h"
388 # undef STYLE_STRUCT
389 return Nothing();
391 #endif // DEBUG
393 ComputedStyle* ComputedStyle::GetCachedLazyPseudoStyle(
394 PseudoStyleType aPseudo) const {
395 MOZ_ASSERT(PseudoStyle::IsPseudoElement(aPseudo));
397 if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(aPseudo)) {
398 return nullptr;
401 return mCachedInheritingStyles.Lookup(aPseudo);
404 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoComputedValuesMallocEnclosingSizeOf)
406 void ComputedStyle::AddSizeOfIncludingThis(nsWindowSizes& aSizes,
407 size_t* aCVsSize) const {
408 // Note: |this| sits within a servo_arc::Arc, i.e. it is preceded by a
409 // refcount. So we need to measure it with a function that can handle an
410 // interior pointer. We use ServoComputedValuesMallocEnclosingSizeOf to
411 // clearly identify in DMD's output the memory measured here.
412 *aCVsSize += ServoComputedValuesMallocEnclosingSizeOf(this);
413 mSource.AddSizeOfExcludingThis(aSizes);
414 mCachedInheritingStyles.AddSizeOfIncludingThis(aSizes, aCVsSize);
417 #ifdef DEBUG
418 bool ComputedStyle::EqualForCachedAnonymousContentStyle(
419 const ComputedStyle& aOther) const {
420 // One thing we can't add UA rules to prevent is different -x-lang
421 // values being inherited in. So we use this FFI function function rather
422 // than rely on CalcStyleDifference, which can't tell us which specific
423 // properties have changed.
424 return Servo_ComputedValues_EqualForCachedAnonymousContentStyle(this,
425 &aOther);
428 void ComputedStyle::DumpMatchedRules() const {
429 Servo_ComputedValues_DumpMatchedRules(this);
431 #endif
433 } // namespace mozilla