tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ComputedStyle.cpp (21171B)


      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/. */
      6 
      7 /* the interface (to internal code) for retrieving computed style data */
      8 
      9 #include "mozilla/ComputedStyle.h"
     10 
     11 #include "RubyUtils.h"
     12 #include "mozilla/ComputedStyleInlines.h"
     13 #include "mozilla/DebugOnly.h"
     14 #include "mozilla/Maybe.h"
     15 #include "mozilla/Preferences.h"
     16 #include "mozilla/ProfilerLabels.h"
     17 #include "mozilla/ReflowInput.h"
     18 #include "mozilla/ToString.h"
     19 #include "mozilla/dom/Document.h"
     20 #include "nsCOMPtr.h"
     21 #include "nsCSSAnonBoxes.h"
     22 #include "nsCSSPseudoElements.h"
     23 #include "nsCSSVisitedDependentPropList.h"
     24 #include "nsCoord.h"
     25 #include "nsFontMetrics.h"
     26 #include "nsLayoutUtils.h"
     27 #include "nsPresContext.h"
     28 #include "nsPrintfCString.h"
     29 #include "nsString.h"
     30 #include "nsStyleConsts.h"
     31 #include "nsStyleStruct.h"
     32 #include "nsStyleStructInlines.h"
     33 #include "nsStyleStructList.h"
     34 #include "nsWindowSizes.h"
     35 
     36 // Ensure the binding function declarations in ComputedStyle.h matches
     37 // those in ServoBindings.h.
     38 #include "mozilla/ServoBindings.h"
     39 
     40 namespace mozilla {
     41 
     42 ComputedStyle::ComputedStyle(PseudoStyleType aPseudoType,
     43                             ServoComputedDataForgotten aComputedValues)
     44    : mSource(aComputedValues), mPseudoType(aPseudoType) {}
     45 
     46 // If a struct returned nsChangeHint_UpdateContainingBlock, that means that one
     47 // property's influence on whether we're a containing block for abs-pos or
     48 // fixed-pos elements has changed.
     49 //
     50 // However, we only need to return the hint if the overall computation of
     51 // whether we establish a containing block has really changed.
     52 static bool ContainingBlockMayHaveChanged(const ComputedStyle& aOldStyle,
     53                                          const ComputedStyle& aNewStyle) {
     54  const auto& oldDisp = *aOldStyle.StyleDisplay();
     55  const auto& newDisp = *aNewStyle.StyleDisplay();
     56 
     57  if (oldDisp.IsPositionedStyle() != newDisp.IsPositionedStyle()) {
     58    // XXX This check can probably be moved to after the fixedCB check, since
     59    // IsPositionedStyle() is also only relevant for non-svg text frame
     60    // subtrees.
     61    return true;
     62  }
     63 
     64  const bool fixedCB = aOldStyle.IsFixedPosContainingBlockForNonSVGTextFrames();
     65  if (fixedCB != aNewStyle.IsFixedPosContainingBlockForNonSVGTextFrames()) {
     66    return true;
     67  }
     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
     70  // subset of this.
     71  if (fixedCB) {
     72    return false;
     73  }
     74 
     75  // Note that neither of these two following sets of frames
     76  // (transform-supporting and layout-and-paint-supporting frames) is a subset
     77  // of the other, because table frames support contain: layout/paint but not
     78  // transforms (which are instead inherited to the table wrapper), and quite a
     79  // few frame types support transforms but not contain: layout/paint (e.g.,
     80  // table rows and row groups, many SVG frames).
     81  if (oldDisp.IsFixedPosContainingBlockForTransformSupportingFrames() !=
     82      newDisp.IsFixedPosContainingBlockForTransformSupportingFrames()) {
     83    return true;
     84  }
     85  if (oldDisp
     86          .IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames() !=
     87      newDisp
     88          .IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames()) {
     89    return true;
     90  }
     91  return false;
     92 }
     93 
     94 nsChangeHint ComputedStyle::CalcStyleDifference(const ComputedStyle& aNewStyle,
     95                                                uint32_t* aEqualStructs) const {
     96  AUTO_PROFILER_LABEL_HOT("ComputedStyle::CalcStyleDifference", LAYOUT);
     97  static_assert(StyleStructConstants::kStyleStructCount <= 32,
     98                "aEqualStructs is not big enough");
     99 
    100  *aEqualStructs = 0;
    101 
    102  nsChangeHint hint = nsChangeHint(0);
    103  // We must always ensure that we populate the structs on the new style
    104  // context that are filled in on the old context, so that if we get
    105  // two style changes in succession, the second of which causes a real
    106  // style change, the PeekStyleData doesn't return null (implying that
    107  // nobody ever looked at that struct's data).  In other words, we
    108  // can't skip later structs if we get a big change up front, because
    109  // we could later get a small change in one of those structs that we
    110  // don't want to miss.
    111 
    112  DebugOnly<uint32_t> structsFound = 0;
    113 
    114  DebugOnly<int> styleStructCount = 0;
    115 
    116  // Servo's optimization to stop the cascade when there are no style changes
    117  // that children need to be recascade for relies on comparing all of the
    118  // structs, not just those that are returned from PeekStyleData, although
    119  // if PeekStyleData does return null we could avoid to accumulate any change
    120  // hints for those structs.
    121  //
    122  // FIXME(emilio): Reintroduce that optimization either for all kind of structs
    123  // after bug 1368290 with a weak parent pointer from text, or just for reset
    124  // structs.
    125 #define STYLE_STRUCT_BIT(name_) \
    126  StyleStructConstants::BitFor(StyleStructID::name_)
    127 
    128 #define EXPAND(...) __VA_ARGS__
    129 #define DO_STRUCT_DIFFERENCE_WITH_ARGS(struct_, extra_args_)               \
    130  PR_BEGIN_MACRO                                                           \
    131  const nsStyle##struct_* this##struct_ = Style##struct_();                \
    132  structsFound |= STYLE_STRUCT_BIT(struct_);                               \
    133                                                                           \
    134  const nsStyle##struct_* other##struct_ = aNewStyle.Style##struct_();     \
    135  if (this##struct_ == other##struct_) {                                   \
    136    /* The very same struct, so we know that there will be no */           \
    137    /* differences.                                           */           \
    138    *aEqualStructs |= STYLE_STRUCT_BIT(struct_);                           \
    139  } else {                                                                 \
    140    nsChangeHint difference =                                              \
    141        this##struct_->CalcDifference(*other##struct_ EXPAND extra_args_); \
    142    hint |= difference;                                                    \
    143    if (!difference) {                                                     \
    144      *aEqualStructs |= STYLE_STRUCT_BIT(struct_);                         \
    145    }                                                                      \
    146  }                                                                        \
    147  styleStructCount++;                                                      \
    148  PR_END_MACRO
    149 #define DO_STRUCT_DIFFERENCE(struct_) \
    150  DO_STRUCT_DIFFERENCE_WITH_ARGS(struct_, ())
    151 
    152  // FIXME: The order of these DO_STRUCT_DIFFERENCE calls is no longer
    153  // significant.  With a small amount of effort, we could replace them with a
    154  // #include "nsStyleStructList.h".
    155  DO_STRUCT_DIFFERENCE_WITH_ARGS(Display, (, *this));
    156  DO_STRUCT_DIFFERENCE(XUL);
    157  DO_STRUCT_DIFFERENCE(Column);
    158  DO_STRUCT_DIFFERENCE(Content);
    159  DO_STRUCT_DIFFERENCE(UI);
    160  DO_STRUCT_DIFFERENCE(Visibility);
    161  DO_STRUCT_DIFFERENCE(Outline);
    162  DO_STRUCT_DIFFERENCE(TableBorder);
    163  DO_STRUCT_DIFFERENCE(Table);
    164  DO_STRUCT_DIFFERENCE(UIReset);
    165  DO_STRUCT_DIFFERENCE(Text);
    166  DO_STRUCT_DIFFERENCE_WITH_ARGS(List, (, *this));
    167  DO_STRUCT_DIFFERENCE(SVGReset);
    168  DO_STRUCT_DIFFERENCE(SVG);
    169  DO_STRUCT_DIFFERENCE_WITH_ARGS(Position, (, *this));
    170  DO_STRUCT_DIFFERENCE(Font);
    171  DO_STRUCT_DIFFERENCE(Margin);
    172  DO_STRUCT_DIFFERENCE(Padding);
    173  DO_STRUCT_DIFFERENCE(Border);
    174  DO_STRUCT_DIFFERENCE(TextReset);
    175  DO_STRUCT_DIFFERENCE(Effects);
    176  DO_STRUCT_DIFFERENCE(Background);
    177  DO_STRUCT_DIFFERENCE(Page);
    178 
    179 #undef DO_STRUCT_DIFFERENCE
    180 #undef DO_STRUCT_DIFFERENCE_WITH_ARGS
    181 #undef EXPAND
    182 
    183  MOZ_ASSERT(styleStructCount == StyleStructConstants::kStyleStructCount,
    184             "missing a call to DO_STRUCT_DIFFERENCE");
    185 
    186  // Note that we do not check whether this->RelevantLinkVisited() !=
    187  // aNewContext->RelevantLinkVisited(); we don't need to since
    188  // nsCSSFrameConstructor::DoContentStateChanged always adds
    189  // nsChangeHint_RepaintFrame for ElementState::VISITED changes (and
    190  // needs to, since HasStateDependentStyle probably doesn't work right
    191  // for ElementState::VISITED).  Hopefully this doesn't actually
    192  // expose whether links are visited to performance tests since all
    193  // link coloring happens asynchronously at a time when it's hard for
    194  // the page to measure.
    195  // However, we do need to compute the larger of the changes that can
    196  // happen depending on whether the link is visited or unvisited, since
    197  // doing only the one that's currently appropriate would expose which
    198  // links are in history to easy performance measurement.  Therefore,
    199  // here, we add nsChangeHint_RepaintFrame hints (the maximum for
    200  // things that can depend on :visited) for the properties on which we
    201  // call GetVisitedDependentColor.
    202  const ComputedStyle* thisVis = GetStyleIfVisited();
    203  const ComputedStyle* otherVis = aNewStyle.GetStyleIfVisited();
    204  if (!thisVis != !otherVis) {
    205    // One style has a style-if-visited and the other doesn't.
    206    // Presume a difference.
    207 #define CLEAR_STRUCT_BIT(name_, fields_) \
    208  *aEqualStructs &= ~STYLE_STRUCT_BIT(name_);
    209    FOR_EACH_VISITED_DEPENDENT_STYLE_STRUCT(CLEAR_STRUCT_BIT)
    210 #undef CLEAR_STRUCT_BIT
    211    hint |= nsChangeHint_RepaintFrame;
    212  } else if (thisVis) {
    213    // Both styles have a style-if-visited.
    214    bool change = false;
    215 
    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 CHECK_VISITED_STYLE_STRUCT(name_, fields_)                   \
    223  {                                                                  \
    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;                                                 \
    229    }                                                                \
    230  }
    231    FOR_EACH_VISITED_DEPENDENT_STYLE_STRUCT(CHECK_VISITED_STYLE_STRUCT)
    232 #undef CHECK_VISITED_STYLE_STRUCT
    233 #undef STYLE_FIELD
    234 #undef STYLE_STRUCT_BIT
    235 
    236    if (change) {
    237      hint |= nsChangeHint_RepaintFrame;
    238    }
    239  }
    240 
    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;
    247    }
    248  }
    249 
    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;
    260    }
    261  }
    262 
    263  MOZ_ASSERT(NS_IsHintSubset(hint, nsChangeHint_AllHints),
    264             "Added a new hint without bumping AllHints?");
    265  return hint & ~nsChangeHint_NeutralChange;
    266 }
    267 
    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("  ");
    275  }
    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()));
    279  }
    280 
    281  fprintf_stderr(out, "%s{ServoComputedData}\n", str.get());
    282 }
    283 #endif
    284 
    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());
    294  }
    295  return colors[0];
    296 }
    297 
    298 static nscolor ExtractColor(const ComputedStyle& aStyle,
    299                            const StyleAbsoluteColor& aColor) {
    300  return aColor.ToColor();
    301 }
    302 
    303 static nscolor ExtractColor(const ComputedStyle& aStyle,
    304                            const StyleColor& aColor) {
    305  return aColor.CalcColor(aStyle);
    306 }
    307 
    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());
    314  }
    315  return ExtractColor(aStyle, aColor.AsColor());
    316 }
    317 
    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);
    323 }
    324 
    325 #define STYLE_FIELD(struct_, field_) aField == &struct_::field_ ||
    326 #define GENERATE_VISITED_COLOR_TEMPLATE(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        });                                                                    \
    338  }
    339 FOR_EACH_VISITED_DEPENDENT_STYLE_STRUCT(GENERATE_VISITED_COLOR_TEMPLATE)
    340 #undef GENERATE_VISITED_COLOR_TEMPLATE
    341 #undef STYLE_FIELD
    342 
    343 struct ColorIndexSet {
    344  uint8_t colorIndex, alphaIndex;
    345 };
    346 
    347 static const ColorIndexSet gVisitedIndices[2] = {{0, 0}, {1, 0}};
    348 
    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;
    358  }
    359 
    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];
    363 
    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));
    368 }
    369 
    370 #ifdef DEBUG
    371 /* static */ const char* ComputedStyle::StructName(StyleStructID aSID) {
    372  switch (aSID) {
    373 #  define CASE_STRUCT(name_)   \
    374    case StyleStructID::name_: \
    375      return #name_;
    376    FOR_EACH_STYLE_STRUCT(CASE_STRUCT, CASE_STRUCT)
    377 #  undef CASE_STRUCT
    378    default:
    379      return "Unknown";
    380  }
    381 }
    382 
    383 /* static */
    384 Maybe<StyleStructID> ComputedStyle::LookupStruct(const nsACString& aName) {
    385 #  define CHECK_STRUCT(name_) \
    386    if (aName.EqualsLiteral(#name_)) return Some(StyleStructID::name_);
    387  FOR_EACH_STYLE_STRUCT(CHECK_STRUCT, CHECK_STRUCT)
    388 #  undef CHECK_STRUCT
    389  return Nothing();
    390 }
    391 #endif  // DEBUG
    392 
    393 ComputedStyle* ComputedStyle::GetCachedLazyPseudoStyle(
    394    PseudoStyleType aPseudo) const {
    395  MOZ_ASSERT(PseudoStyle::IsPseudoElement(aPseudo));
    396 
    397  if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(aPseudo)) {
    398    return nullptr;
    399  }
    400 
    401  return mCachedInheritingStyles.Lookup(aPseudo);
    402 }
    403 
    404 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoComputedValuesMallocEnclosingSizeOf)
    405 
    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);
    415 }
    416 
    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);
    426 }
    427 
    428 void ComputedStyle::DumpMatchedRules() const {
    429  Servo_ComputedValues_DumpMatchedRules(this);
    430 }
    431 #endif
    432 
    433 bool ComputedStyle::HasAnchorPosReference() const {
    434  const auto* pos = StylePosition();
    435  if (pos->mPositionAnchor.IsIdent()) {
    436    // Short circuit if there's an explicit default anchor defined,
    437    // even if it may not end up being referenced. If this early return is
    438    // removed, we'll need to handle mPositionArea explicitly.
    439    return true;
    440  }
    441 
    442  if (pos->mPositionAnchor.IsAuto()) {
    443    if (!pos->mPositionArea.IsNone()) {
    444      // Position area is relative to an anchor.
    445      return true;
    446    }
    447 
    448    // Check if anchor-center is used in alignment properties, directly
    449    // accessing members rather than using UsedAlign* because legacy values
    450    // can't resolve to anchor-center.
    451    const auto alignSelfValue =
    452        pos->mAlignSelf._0 & ~StyleAlignFlags::FLAG_BITS;
    453    const auto justifySelfValue =
    454        pos->mJustifySelf._0 & ~StyleAlignFlags::FLAG_BITS;
    455    if (alignSelfValue == StyleAlignFlags::ANCHOR_CENTER ||
    456        justifySelfValue == StyleAlignFlags::ANCHOR_CENTER) {
    457      return true;
    458    }
    459  }
    460 
    461  // Now check if any property that can use anchor() or anchor-size()
    462  // does use any. Note that it's valid to specify e.g. left: anchor(left);
    463  // but without specifying position-anchor, in which case the function
    464  // makes no anchor reference.
    465  return pos->mOffset.Any([](const StyleInset& aInset) {
    466    return aInset.HasAnchorPositioningFunction();
    467  }) || pos->mWidth.HasAnchorPositioningFunction() ||
    468         pos->mHeight.HasAnchorPositioningFunction() ||
    469         pos->mMinWidth.HasAnchorPositioningFunction() ||
    470         pos->mMinHeight.HasAnchorPositioningFunction() ||
    471         pos->mMaxWidth.HasAnchorPositioningFunction() ||
    472         pos->mMaxHeight.HasAnchorPositioningFunction() ||
    473         StyleMargin()->mMargin.Any([](const ::mozilla::StyleMargin& aMargin) {
    474           return aMargin.HasAnchorPositioningFunction();
    475         });
    476 }
    477 
    478 // XXX: This is a broad-stroke method to return true if the referenced anchors
    479 // in two computed style may differ. Since we do not get the actual anchor
    480 // names, we do not know if the difference is just a position/size/margin change
    481 // or if indeed the anchors changed. So we will get false positives here, hence
    482 // "maybe".
    483 bool ComputedStyle::MaybeAnchorPosReferencesDiffer(
    484    const ComputedStyle* aOther) const {
    485  if (!HasAnchorPosReference() || !aOther->HasAnchorPosReference()) {
    486    return true;
    487  }
    488 
    489  const auto* pos = StylePosition();
    490  const auto* otherPos = aOther->StylePosition();
    491  if (pos->mOffset != otherPos->mOffset || pos->mWidth != otherPos->mWidth ||
    492      pos->mHeight != otherPos->mHeight ||
    493      pos->mMinWidth != otherPos->mMinWidth ||
    494      pos->mMinHeight != otherPos->mMinHeight ||
    495      pos->mMaxWidth != otherPos->mMaxWidth ||
    496      pos->mMaxHeight != otherPos->mMaxHeight ||
    497      pos->mPositionAnchor != otherPos->mPositionAnchor) {
    498    return true;
    499  }
    500 
    501  if (StyleMargin()->mMargin != aOther->StyleMargin()->mMargin) {
    502    return true;
    503  }
    504 
    505  return false;
    506 }
    507 
    508 }  // namespace mozilla