summaryrefslogtreecommitdiffstats
path: root/layout/style/ComputedStyle.cpp
blob: 46953bf34d6455e158faae5c92248342efd8df4c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
/* -*- 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/. */

/* the interface (to internal code) for retrieving computed style data */

#include "mozilla/ComputedStyle.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Maybe.h"
#include "mozilla/ToString.h"

#include "nsCSSAnonBoxes.h"
#include "nsCSSPseudoElements.h"
#include "nsFontMetrics.h"
#include "nsStyleConsts.h"
#include "nsStyleStruct.h"
#include "nsStyleStructInlines.h"
#include "nsString.h"
#include "nsPresContext.h"
#include "nsWindowSizes.h"

#include "nsCOMPtr.h"

#include "GeckoProfiler.h"
#include "mozilla/dom/Document.h"
#include "nsPrintfCString.h"
#include "RubyUtils.h"
#include "mozilla/ComputedStyleInlines.h"
#include "mozilla/Preferences.h"

#include "mozilla/ReflowInput.h"
#include "nsLayoutUtils.h"
#include "nsCoord.h"

// Ensure the binding function declarations in ComputedStyle.h matches
// those in ServoBindings.h.
#include "mozilla/ServoBindings.h"

namespace mozilla {

ComputedStyle::ComputedStyle(PseudoStyleType aPseudoType,
                             ServoComputedDataForgotten aComputedValues)
    : mSource(aComputedValues), mPseudoType(aPseudoType) {}

// If a struct returned nsChangeHint_UpdateContainingBlock, that means that one
// property's influence on whether we're a containing block for abs-pos or
// fixed-pos elements has changed.
//
// However, we only need to return the hint if the overall computation of
// whether we establish a containing block has really changed.
static bool ContainingBlockMayHaveChanged(const ComputedStyle& aOldStyle,
                                          const ComputedStyle& aNewStyle) {
  auto* oldDisp = aOldStyle.StyleDisplay();
  auto* newDisp = aNewStyle.StyleDisplay();

  if (oldDisp->IsPositionedStyle() != newDisp->IsPositionedStyle()) {
    return true;
  }

  bool fixedCB =
      oldDisp->IsFixedPosContainingBlockForNonSVGTextFrames(aOldStyle);
  if (fixedCB !=
      newDisp->IsFixedPosContainingBlockForNonSVGTextFrames(aNewStyle)) {
    return true;
  }
  // If we were both before and after a fixed-pos containing-block that means
  // that everything else doesn't matter, since all the other conditions are a
  // subset of this.
  if (fixedCB) {
    return false;
  }
  // Note that neither of these two following sets of frames
  // (transform-supporting and layout-and-paint-supporting frames) is a subset
  // of the other, because table frames support contain: layout/paint but not
  // transforms (which are instead inherited to the table wrapper), and quite a
  // few frame types support transforms but not contain: layout/paint (e.g.,
  // table rows and row groups, many SVG frames).
  if (oldDisp->IsFixedPosContainingBlockForTransformSupportingFrames() !=
      newDisp->IsFixedPosContainingBlockForTransformSupportingFrames()) {
    return true;
  }
  if (oldDisp
          ->IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames() !=
      newDisp
          ->IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames()) {
    return true;
  }
  return false;
}

nsChangeHint ComputedStyle::CalcStyleDifference(const ComputedStyle& aNewStyle,
                                                uint32_t* aEqualStructs) const {
  AUTO_PROFILER_LABEL("ComputedStyle::CalcStyleDifference", LAYOUT);
  static_assert(StyleStructConstants::kStyleStructCount <= 32,
                "aEqualStructs is not big enough");

  *aEqualStructs = 0;

  nsChangeHint hint = nsChangeHint(0);
  // We must always ensure that we populate the structs on the new style
  // context that are filled in on the old context, so that if we get
  // two style changes in succession, the second of which causes a real
  // style change, the PeekStyleData doesn't return null (implying that
  // nobody ever looked at that struct's data).  In other words, we
  // can't skip later structs if we get a big change up front, because
  // we could later get a small change in one of those structs that we
  // don't want to miss.

  DebugOnly<uint32_t> structsFound = 0;

  DebugOnly<int> styleStructCount = 0;

  // Servo's optimization to stop the cascade when there are no style changes
  // that children need to be recascade for relies on comparing all of the
  // structs, not just those that are returned from PeekStyleData, although
  // if PeekStyleData does return null we could avoid to accumulate any change
  // hints for those structs.
  //
  // FIXME(emilio): Reintroduce that optimization either for all kind of structs
  // after bug 1368290 with a weak parent pointer from text, or just for reset
  // structs.
#define STYLE_STRUCT_BIT(name_) \
  StyleStructConstants::BitFor(StyleStructID::name_)

#define EXPAND(...) __VA_ARGS__
#define DO_STRUCT_DIFFERENCE_WITH_ARGS(struct_, extra_args_)               \
  PR_BEGIN_MACRO                                                           \
  const nsStyle##struct_* this##struct_ = Style##struct_();                \
  structsFound |= STYLE_STRUCT_BIT(struct_);                               \
                                                                           \
  const nsStyle##struct_* other##struct_ = aNewStyle.Style##struct_();     \
  if (this##struct_ == other##struct_) {                                   \
    /* The very same struct, so we know that there will be no */           \
    /* differences.                                           */           \
    *aEqualStructs |= STYLE_STRUCT_BIT(struct_);                           \
  } else {                                                                 \
    nsChangeHint difference =                                              \
        this##struct_->CalcDifference(*other##struct_ EXPAND extra_args_); \
    hint |= difference;                                                    \
    if (!difference) {                                                     \
      *aEqualStructs |= STYLE_STRUCT_BIT(struct_);                         \
    }                                                                      \
  }                                                                        \
  styleStructCount++;                                                      \
  PR_END_MACRO
#define DO_STRUCT_DIFFERENCE(struct_) \
  DO_STRUCT_DIFFERENCE_WITH_ARGS(struct_, ())

  // FIXME: The order of these DO_STRUCT_DIFFERENCE calls is no longer
  // significant.  With a small amount of effort, we could replace them with a
  // #include "nsStyleStructList.h".
  DO_STRUCT_DIFFERENCE_WITH_ARGS(Display, (, *StylePosition()));
  DO_STRUCT_DIFFERENCE(XUL);
  DO_STRUCT_DIFFERENCE(Column);
  DO_STRUCT_DIFFERENCE(Content);
  DO_STRUCT_DIFFERENCE(UI);
  DO_STRUCT_DIFFERENCE(Visibility);
  DO_STRUCT_DIFFERENCE(Outline);
  DO_STRUCT_DIFFERENCE(TableBorder);
  DO_STRUCT_DIFFERENCE(Table);
  DO_STRUCT_DIFFERENCE(UIReset);
  DO_STRUCT_DIFFERENCE(Text);
  DO_STRUCT_DIFFERENCE_WITH_ARGS(List, (, *StyleDisplay()));
  DO_STRUCT_DIFFERENCE(SVGReset);
  DO_STRUCT_DIFFERENCE(SVG);
  DO_STRUCT_DIFFERENCE_WITH_ARGS(Position, (, *StyleVisibility()));
  DO_STRUCT_DIFFERENCE(Font);
  DO_STRUCT_DIFFERENCE(Margin);
  DO_STRUCT_DIFFERENCE(Padding);
  DO_STRUCT_DIFFERENCE(Border);
  DO_STRUCT_DIFFERENCE(TextReset);
  DO_STRUCT_DIFFERENCE(Effects);
  DO_STRUCT_DIFFERENCE(Background);

#undef DO_STRUCT_DIFFERENCE
#undef DO_STRUCT_DIFFERENCE_WITH_ARGS
#undef EXPAND

  MOZ_ASSERT(styleStructCount == StyleStructConstants::kStyleStructCount,
             "missing a call to DO_STRUCT_DIFFERENCE");

  // Note that we do not check whether this->RelevantLinkVisited() !=
  // aNewContext->RelevantLinkVisited(); we don't need to since
  // nsCSSFrameConstructor::DoContentStateChanged always adds
  // nsChangeHint_RepaintFrame for NS_EVENT_STATE_VISITED changes (and
  // needs to, since HasStateDependentStyle probably doesn't work right
  // for NS_EVENT_STATE_VISITED).  Hopefully this doesn't actually
  // expose whether links are visited to performance tests since all
  // link coloring happens asynchronously at a time when it's hard for
  // the page to measure.
  // However, we do need to compute the larger of the changes that can
  // happen depending on whether the link is visited or unvisited, since
  // doing only the one that's currently appropriate would expose which
  // links are in history to easy performance measurement.  Therefore,
  // here, we add nsChangeHint_RepaintFrame hints (the maximum for
  // things that can depend on :visited) for the properties on which we
  // call GetVisitedDependentColor.
  const ComputedStyle* thisVis = GetStyleIfVisited();
  const ComputedStyle* otherVis = aNewStyle.GetStyleIfVisited();
  if (!thisVis != !otherVis) {
    // One style has a style-if-visited and the other doesn't.
    // Presume a difference.
#define STYLE_STRUCT(name_, fields_) *aEqualStructs &= ~STYLE_STRUCT_BIT(name_);
#include "nsCSSVisitedDependentPropList.h"
#undef STYLE_STRUCT
    hint |= nsChangeHint_RepaintFrame;
  } else if (thisVis) {
    // Both styles have a style-if-visited.
    bool change = false;

    // NB: Calling Peek on |this|, not |thisVis|, since callers may look
    // at a struct on |this| without looking at the same struct on
    // |thisVis| (including this function if we skip one of these checks
    // due to change being true already or due to the old style not having a
    // style-if-visited), but not the other way around.
#define STYLE_FIELD(name_) thisVisStruct->name_ != otherVisStruct->name_
#define STYLE_STRUCT(name_, fields_)                                 \
  {                                                                  \
    const nsStyle##name_* thisVisStruct = thisVis->Style##name_();   \
    const nsStyle##name_* otherVisStruct = otherVis->Style##name_(); \
    if (MOZ_FOR_EACH_SEPARATED(STYLE_FIELD, (||), (), fields_)) {    \
      *aEqualStructs &= ~STYLE_STRUCT_BIT(name_);                    \
      change = true;                                                 \
    }                                                                \
  }
#include "nsCSSVisitedDependentPropList.h"
#undef STYLE_STRUCT
#undef STYLE_FIELD
#undef STYLE_STRUCT_BIT

    if (change) {
      hint |= nsChangeHint_RepaintFrame;
    }
  }

  if (hint & nsChangeHint_UpdateContainingBlock) {
    if (!ContainingBlockMayHaveChanged(*this, aNewStyle)) {
      // While some styles that cause the frame to be a containing block
      // has changed, the overall result cannot have changed (no matter
      // what the frame type is).
      hint &= ~nsChangeHint_UpdateContainingBlock;
    }
  }

  if (HasAuthorSpecifiedBorderOrBackground() !=
          aNewStyle.HasAuthorSpecifiedBorderOrBackground() &&
      StyleDisplay()->HasAppearance()) {
    // A background-specified change may cause padding to change, so we may need
    // to reflow.  We use the same hint here as we do for "appearance" changes.
    hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
  }

  MOZ_ASSERT(NS_IsHintSubset(hint, nsChangeHint_AllHints),
             "Added a new hint without bumping AllHints?");
  return hint & ~nsChangeHint_NeutralChange;
}

#ifdef DEBUG
void ComputedStyle::List(FILE* out, int32_t aIndent) {
  nsAutoCString str;
  // Indent
  int32_t ix;
  for (ix = aIndent; --ix >= 0;) {
    str.AppendLiteral("  ");
  }
  str.Append(nsPrintfCString("%p(%d) parent=%p ", (void*)this, 0, nullptr));
  if (mPseudoType != PseudoStyleType::NotPseudo) {
    str.Append(nsPrintfCString("%s ", ToString(mPseudoType).c_str()));
  }

  fprintf_stderr(out, "%s{ServoComputedData}\n", str.get());
}
#endif

template <typename Func>
static nscolor GetVisitedDependentColorInternal(const ComputedStyle& aStyle,
                                                Func aColorFunc) {
  nscolor colors[2];
  colors[0] = aColorFunc(aStyle);
  if (const ComputedStyle* visitedStyle = aStyle.GetStyleIfVisited()) {
    colors[1] = aColorFunc(*visitedStyle);
    return ComputedStyle::CombineVisitedColors(colors,
                                               aStyle.RelevantLinkVisited());
  }
  return colors[0];
}

static nscolor ExtractColor(const ComputedStyle& aStyle,
                            const StyleRGBA& aColor) {
  return aColor.ToColor();
}

static nscolor ExtractColor(const ComputedStyle& aStyle,
                            const StyleColor& aColor) {
  return aColor.CalcColor(aStyle);
}

// Currently caret-color, the only property in the list which is a ColorOrAuto,
// always maps auto to currentcolor.
static nscolor ExtractColor(const ComputedStyle& aStyle,
                            const StyleColorOrAuto& aColor) {
  if (aColor.IsAuto()) {
    return ExtractColor(aStyle, StyleColor::CurrentColor());
  }
  return ExtractColor(aStyle, aColor.AsColor());
}

static nscolor ExtractColor(const ComputedStyle& aStyle,
                            const StyleSVGPaint& aPaintServer) {
  return aPaintServer.kind.IsColor()
             ? ExtractColor(aStyle, aPaintServer.kind.AsColor())
             : NS_RGBA(0, 0, 0, 0);
}

#define STYLE_FIELD(struct_, field_) aField == &struct_::field_ ||
#define STYLE_STRUCT(name_, fields_)                                           \
  template <>                                                                  \
  nscolor ComputedStyle::GetVisitedDependentColor(decltype(                    \
      nsStyle##name_::MOZ_ARG_1 fields_) nsStyle##name_::*aField) const {      \
    MOZ_ASSERT(MOZ_FOR_EACH(STYLE_FIELD, (nsStyle##name_, ), fields_) false,   \
               "Getting visited-dependent color for a field in nsStyle" #name_ \
               " which is not listed in nsCSSVisitedDependentPropList.h");     \
    return GetVisitedDependentColorInternal(                                   \
        *this, [aField](const ComputedStyle& aStyle) {                         \
          return ExtractColor(aStyle, aStyle.Style##name_()->*aField);         \
        });                                                                    \
  }
#include "nsCSSVisitedDependentPropList.h"
#undef STYLE_STRUCT
#undef STYLE_FIELD

struct ColorIndexSet {
  uint8_t colorIndex, alphaIndex;
};

static const ColorIndexSet gVisitedIndices[2] = {{0, 0}, {1, 0}};

/* static */
nscolor ComputedStyle::CombineVisitedColors(nscolor* aColors,
                                            bool aLinkIsVisited) {
  if (NS_GET_A(aColors[1]) == 0) {
    // If the style-if-visited is transparent, then just use the
    // unvisited style rather than using the (meaningless) color
    // components of the visited style along with a potentially
    // non-transparent alpha value.
    aLinkIsVisited = false;
  }

  // NOTE: We want this code to have as little timing dependence as
  // possible on whether this->RelevantLinkVisited() is true.
  const ColorIndexSet& set = gVisitedIndices[aLinkIsVisited ? 1 : 0];

  nscolor colorColor = aColors[set.colorIndex];
  nscolor alphaColor = aColors[set.alphaIndex];
  return NS_RGBA(NS_GET_R(colorColor), NS_GET_G(colorColor),
                 NS_GET_B(colorColor), NS_GET_A(alphaColor));
}

#ifdef DEBUG
/* static */ const char* ComputedStyle::StructName(StyleStructID aSID) {
  switch (aSID) {
#  define STYLE_STRUCT(name_)  \
    case StyleStructID::name_: \
      return #name_;
#  include "nsStyleStructList.h"
#  undef STYLE_STRUCT
    default:
      return "Unknown";
  }
}

/* static */
Maybe<StyleStructID> ComputedStyle::LookupStruct(const nsACString& aName) {
#  define STYLE_STRUCT(name_) \
    if (aName.EqualsLiteral(#name_)) return Some(StyleStructID::name_);
#  include "nsStyleStructList.h"
#  undef STYLE_STRUCT
  return Nothing();
}
#endif  // DEBUG

ComputedStyle* ComputedStyle::GetCachedLazyPseudoStyle(
    PseudoStyleType aPseudo) const {
  MOZ_ASSERT(PseudoStyle::IsPseudoElement(aPseudo));

  if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(aPseudo)) {
    return nullptr;
  }

  return mCachedInheritingStyles.Lookup(aPseudo);
}

MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoComputedValuesMallocEnclosingSizeOf)

void ComputedStyle::AddSizeOfIncludingThis(nsWindowSizes& aSizes,
                                           size_t* aCVsSize) const {
  // Note: |this| sits within a servo_arc::Arc, i.e. it is preceded by a
  // refcount. So we need to measure it with a function that can handle an
  // interior pointer. We use ServoComputedValuesMallocEnclosingSizeOf to
  // clearly identify in DMD's output the memory measured here.
  *aCVsSize += ServoComputedValuesMallocEnclosingSizeOf(this);
  mSource.AddSizeOfExcludingThis(aSizes);
  mCachedInheritingStyles.AddSizeOfIncludingThis(aSizes, aCVsSize);
}

#ifdef DEBUG
bool ComputedStyle::EqualForCachedAnonymousContentStyle(
    const ComputedStyle& aOther) const {
  // One thing we can't add UA rules to prevent is different -x-lang
  // values being inherited in.  So we use this FFI function function rather
  // than rely on CalcStyleDifference, which can't tell us which specific
  // properties have changed.
  return Servo_ComputedValues_EqualForCachedAnonymousContentStyle(this,
                                                                  &aOther);
}

#endif

}  // namespace mozilla