layout/style/nsStyleContext.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/style/nsStyleContext.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,880 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 + 
     1.9 +/* the interface (to internal code) for retrieving computed style data */
    1.10 +
    1.11 +#include "mozilla/DebugOnly.h"
    1.12 +
    1.13 +#include "nsCSSAnonBoxes.h"
    1.14 +#include "nsStyleConsts.h"
    1.15 +#include "nsString.h"
    1.16 +#include "nsPresContext.h"
    1.17 +#include "nsIStyleRule.h"
    1.18 +
    1.19 +#include "nsCOMPtr.h"
    1.20 +#include "nsStyleSet.h"
    1.21 +#include "nsIPresShell.h"
    1.22 +
    1.23 +#include "nsRuleNode.h"
    1.24 +#include "nsStyleContext.h"
    1.25 +#include "nsStyleAnimation.h"
    1.26 +#include "GeckoProfiler.h"
    1.27 +
    1.28 +#ifdef DEBUG
    1.29 +// #define NOISY_DEBUG
    1.30 +#endif
    1.31 +
    1.32 +using namespace mozilla;
    1.33 +
    1.34 +//----------------------------------------------------------------------
    1.35 +
    1.36 +
    1.37 +nsStyleContext::nsStyleContext(nsStyleContext* aParent,
    1.38 +                               nsIAtom* aPseudoTag,
    1.39 +                               nsCSSPseudoElements::Type aPseudoType,
    1.40 +                               nsRuleNode* aRuleNode,
    1.41 +                               bool aSkipFlexItemStyleFixup)
    1.42 +  : mParent(aParent),
    1.43 +    mChild(nullptr),
    1.44 +    mEmptyChild(nullptr),
    1.45 +    mPseudoTag(aPseudoTag),
    1.46 +    mRuleNode(aRuleNode),
    1.47 +    mAllocations(nullptr),
    1.48 +    mCachedResetData(nullptr),
    1.49 +    mBits(((uint64_t)aPseudoType) << NS_STYLE_CONTEXT_TYPE_SHIFT),
    1.50 +    mRefCnt(0)
    1.51 +{
    1.52 +  // This check has to be done "backward", because if it were written the
    1.53 +  // more natural way it wouldn't fail even when it needed to.
    1.54 +  static_assert((UINT64_MAX >> NS_STYLE_CONTEXT_TYPE_SHIFT) >=
    1.55 +                nsCSSPseudoElements::ePseudo_MAX,
    1.56 +                "pseudo element bits no longer fit in a uint64_t");
    1.57 +  MOZ_ASSERT(aRuleNode);
    1.58 +
    1.59 +  mNextSibling = this;
    1.60 +  mPrevSibling = this;
    1.61 +  if (mParent) {
    1.62 +    mParent->AddRef();
    1.63 +    mParent->AddChild(this);
    1.64 +#ifdef DEBUG
    1.65 +    nsRuleNode *r1 = mParent->RuleNode(), *r2 = aRuleNode;
    1.66 +    while (r1->GetParent())
    1.67 +      r1 = r1->GetParent();
    1.68 +    while (r2->GetParent())
    1.69 +      r2 = r2->GetParent();
    1.70 +    NS_ASSERTION(r1 == r2, "must be in the same rule tree as parent");
    1.71 +#endif
    1.72 +  }
    1.73 +
    1.74 +  mRuleNode->AddRef();
    1.75 +  mRuleNode->SetUsedDirectly(); // before ApplyStyleFixups()!
    1.76 +
    1.77 +  ApplyStyleFixups(aSkipFlexItemStyleFixup);
    1.78 +
    1.79 +  #define eStyleStruct_LastItem (nsStyleStructID_Length - 1)
    1.80 +  NS_ASSERTION(NS_STYLE_INHERIT_MASK & NS_STYLE_INHERIT_BIT(LastItem),
    1.81 +               "NS_STYLE_INHERIT_MASK must be bigger, and other bits shifted");
    1.82 +  #undef eStyleStruct_LastItem
    1.83 +}
    1.84 +
    1.85 +nsStyleContext::~nsStyleContext()
    1.86 +{
    1.87 +  NS_ASSERTION((nullptr == mChild) && (nullptr == mEmptyChild), "destructing context with children");
    1.88 +
    1.89 +  nsPresContext *presContext = mRuleNode->PresContext();
    1.90 +
    1.91 +  mRuleNode->Release();
    1.92 +
    1.93 +  presContext->PresShell()->StyleSet()->
    1.94 +    NotifyStyleContextDestroyed(presContext, this);
    1.95 +
    1.96 +  if (mParent) {
    1.97 +    mParent->RemoveChild(this);
    1.98 +    mParent->Release();
    1.99 +  }
   1.100 +
   1.101 +  // Free up our data structs.
   1.102 +  mCachedInheritedData.DestroyStructs(mBits, presContext);
   1.103 +  if (mCachedResetData) {
   1.104 +    mCachedResetData->Destroy(mBits, presContext);
   1.105 +  }
   1.106 +
   1.107 +  FreeAllocations(presContext);
   1.108 +}
   1.109 +
   1.110 +void nsStyleContext::AddChild(nsStyleContext* aChild)
   1.111 +{
   1.112 +  NS_ASSERTION(aChild->mPrevSibling == aChild &&
   1.113 +               aChild->mNextSibling == aChild,
   1.114 +               "child already in a child list");
   1.115 +
   1.116 +  nsStyleContext **listPtr = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild;
   1.117 +  // Explicitly dereference listPtr so that compiler doesn't have to know that mNextSibling
   1.118 +  // etc. don't alias with what ever listPtr points at.
   1.119 +  nsStyleContext *list = *listPtr;
   1.120 +
   1.121 +  // Insert at the beginning of the list.  See also FindChildWithRules.
   1.122 +  if (list) {
   1.123 +    // Link into existing elements, if there are any.
   1.124 +    aChild->mNextSibling = list;
   1.125 +    aChild->mPrevSibling = list->mPrevSibling;
   1.126 +    list->mPrevSibling->mNextSibling = aChild;
   1.127 +    list->mPrevSibling = aChild;
   1.128 +  }
   1.129 +  (*listPtr) = aChild;
   1.130 +}
   1.131 +
   1.132 +void nsStyleContext::RemoveChild(nsStyleContext* aChild)
   1.133 +{
   1.134 +  NS_PRECONDITION(nullptr != aChild && this == aChild->mParent, "bad argument");
   1.135 +
   1.136 +  nsStyleContext **list = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild;
   1.137 +
   1.138 +  if (aChild->mPrevSibling != aChild) { // has siblings
   1.139 +    if ((*list) == aChild) {
   1.140 +      (*list) = (*list)->mNextSibling;
   1.141 +    }
   1.142 +  } 
   1.143 +  else {
   1.144 +    NS_ASSERTION((*list) == aChild, "bad sibling pointers");
   1.145 +    (*list) = nullptr;
   1.146 +  }
   1.147 +
   1.148 +  aChild->mPrevSibling->mNextSibling = aChild->mNextSibling;
   1.149 +  aChild->mNextSibling->mPrevSibling = aChild->mPrevSibling;
   1.150 +  aChild->mNextSibling = aChild;
   1.151 +  aChild->mPrevSibling = aChild;
   1.152 +}
   1.153 +
   1.154 +already_AddRefed<nsStyleContext>
   1.155 +nsStyleContext::FindChildWithRules(const nsIAtom* aPseudoTag, 
   1.156 +                                   nsRuleNode* aRuleNode,
   1.157 +                                   nsRuleNode* aRulesIfVisited,
   1.158 +                                   bool aRelevantLinkVisited)
   1.159 +{
   1.160 +  NS_ABORT_IF_FALSE(aRulesIfVisited || !aRelevantLinkVisited,
   1.161 +    "aRelevantLinkVisited should only be set when we have a separate style");
   1.162 +  uint32_t threshold = 10; // The # of siblings we're willing to examine
   1.163 +                           // before just giving this whole thing up.
   1.164 +
   1.165 +  nsRefPtr<nsStyleContext> result;
   1.166 +  nsStyleContext *list = aRuleNode->IsRoot() ? mEmptyChild : mChild;
   1.167 +
   1.168 +  if (list) {
   1.169 +    nsStyleContext *child = list;
   1.170 +    do {
   1.171 +      if (child->mRuleNode == aRuleNode &&
   1.172 +          child->mPseudoTag == aPseudoTag &&
   1.173 +          !child->IsStyleIfVisited() &&
   1.174 +          child->RelevantLinkVisited() == aRelevantLinkVisited) {
   1.175 +        bool match = false;
   1.176 +        if (aRulesIfVisited) {
   1.177 +          match = child->GetStyleIfVisited() &&
   1.178 +                  child->GetStyleIfVisited()->mRuleNode == aRulesIfVisited;
   1.179 +        } else {
   1.180 +          match = !child->GetStyleIfVisited();
   1.181 +        }
   1.182 +        if (match) {
   1.183 +          result = child;
   1.184 +          break;
   1.185 +        }
   1.186 +      }
   1.187 +      child = child->mNextSibling;
   1.188 +      threshold--;
   1.189 +      if (threshold == 0)
   1.190 +        break;
   1.191 +    } while (child != list);
   1.192 +  }
   1.193 +
   1.194 +  if (result) {
   1.195 +    if (result != list) {
   1.196 +      // Move result to the front of the list.
   1.197 +      RemoveChild(result);
   1.198 +      AddChild(result);
   1.199 +    }
   1.200 +  }
   1.201 +
   1.202 +  return result.forget();
   1.203 +}
   1.204 +
   1.205 +const void* nsStyleContext::GetCachedStyleData(nsStyleStructID aSID)
   1.206 +{
   1.207 +  const void* cachedData;
   1.208 +  if (nsCachedStyleData::IsReset(aSID)) {
   1.209 +    if (mCachedResetData) {
   1.210 +      cachedData = mCachedResetData->mStyleStructs[aSID];
   1.211 +    } else {
   1.212 +      cachedData = nullptr;
   1.213 +    }
   1.214 +  } else {
   1.215 +    cachedData = mCachedInheritedData.mStyleStructs[aSID];
   1.216 +  }
   1.217 +  return cachedData;
   1.218 +}
   1.219 +
   1.220 +const void* nsStyleContext::StyleData(nsStyleStructID aSID)
   1.221 +{
   1.222 +  const void* cachedData = GetCachedStyleData(aSID);
   1.223 +  if (cachedData)
   1.224 +    return cachedData; // We have computed data stored on this node in the context tree.
   1.225 +  return mRuleNode->GetStyleData(aSID, this, true); // Our rule node will take care of it for us.
   1.226 +}
   1.227 +
   1.228 +// This is an evil evil function, since it forces you to alloc your own separate copy of
   1.229 +// style data!  Do not use this function unless you absolutely have to!  You should avoid
   1.230 +// this at all costs! -dwh
   1.231 +void* 
   1.232 +nsStyleContext::GetUniqueStyleData(const nsStyleStructID& aSID)
   1.233 +{
   1.234 +  // If we already own the struct and no kids could depend on it, then
   1.235 +  // just return it.  (We leak in this case if there are kids -- and this
   1.236 +  // function really shouldn't be called for style contexts that could
   1.237 +  // have kids depending on the data.  ClearStyleData would be OK, but
   1.238 +  // this test for no mChild or mEmptyChild doesn't catch that case.)
   1.239 +  const void *current = StyleData(aSID);
   1.240 +  if (!mChild && !mEmptyChild &&
   1.241 +      !(mBits & nsCachedStyleData::GetBitForSID(aSID)) &&
   1.242 +      GetCachedStyleData(aSID))
   1.243 +    return const_cast<void*>(current);
   1.244 +
   1.245 +  void* result;
   1.246 +  nsPresContext *presContext = PresContext();
   1.247 +  switch (aSID) {
   1.248 +
   1.249 +#define UNIQUE_CASE(c_)                                                       \
   1.250 +  case eStyleStruct_##c_:                                                     \
   1.251 +    result = new (presContext) nsStyle##c_(                                   \
   1.252 +      * static_cast<const nsStyle##c_ *>(current));                           \
   1.253 +    break;
   1.254 +
   1.255 +  UNIQUE_CASE(Display)
   1.256 +  UNIQUE_CASE(Background)
   1.257 +  UNIQUE_CASE(Text)
   1.258 +  UNIQUE_CASE(TextReset)
   1.259 +
   1.260 +#undef UNIQUE_CASE
   1.261 +
   1.262 +  default:
   1.263 +    NS_ERROR("Struct type not supported.  Please find another way to do this if you can!");
   1.264 +    return nullptr;
   1.265 +  }
   1.266 +
   1.267 +  SetStyle(aSID, result);
   1.268 +  mBits &= ~static_cast<uint64_t>(nsCachedStyleData::GetBitForSID(aSID));
   1.269 +
   1.270 +  return result;
   1.271 +}
   1.272 +
   1.273 +void
   1.274 +nsStyleContext::SetStyle(nsStyleStructID aSID, void* aStruct)
   1.275 +{
   1.276 +  // This method should only be called from nsRuleNode!  It is not a public
   1.277 +  // method!
   1.278 +  
   1.279 +  NS_ASSERTION(aSID >= 0 && aSID < nsStyleStructID_Length, "out of bounds");
   1.280 +
   1.281 +  // NOTE:  nsCachedStyleData::GetStyleData works roughly the same way.
   1.282 +  // See the comments there (in nsRuleNode.h) for more details about
   1.283 +  // what this is doing and why.
   1.284 +
   1.285 +  void** dataSlot;
   1.286 +  if (nsCachedStyleData::IsReset(aSID)) {
   1.287 +    if (!mCachedResetData) {
   1.288 +      mCachedResetData = new (mRuleNode->PresContext()) nsResetStyleData;
   1.289 +    }
   1.290 +    dataSlot = &mCachedResetData->mStyleStructs[aSID];
   1.291 +  } else {
   1.292 +    dataSlot = &mCachedInheritedData.mStyleStructs[aSID];
   1.293 +  }
   1.294 +  NS_ASSERTION(!*dataSlot || (mBits & nsCachedStyleData::GetBitForSID(aSID)),
   1.295 +               "Going to leak style data");
   1.296 +  *dataSlot = aStruct;
   1.297 +}
   1.298 +
   1.299 +void
   1.300 +nsStyleContext::ApplyStyleFixups(bool aSkipFlexItemStyleFixup)
   1.301 +{
   1.302 +  // See if we have any text decorations.
   1.303 +  // First see if our parent has text decorations.  If our parent does, then we inherit the bit.
   1.304 +  if (mParent && mParent->HasTextDecorationLines()) {
   1.305 +    mBits |= NS_STYLE_HAS_TEXT_DECORATION_LINES;
   1.306 +  } else {
   1.307 +    // We might have defined a decoration.
   1.308 +    const nsStyleTextReset* text = StyleTextReset();
   1.309 +    uint8_t decorationLine = text->mTextDecorationLine;
   1.310 +    if (decorationLine != NS_STYLE_TEXT_DECORATION_LINE_NONE &&
   1.311 +        decorationLine != NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL) {
   1.312 +      mBits |= NS_STYLE_HAS_TEXT_DECORATION_LINES;
   1.313 +    }
   1.314 +  }
   1.315 +
   1.316 +  if ((mParent && mParent->HasPseudoElementData()) || mPseudoTag) {
   1.317 +    mBits |= NS_STYLE_HAS_PSEUDO_ELEMENT_DATA;
   1.318 +  }
   1.319 +
   1.320 +  // Correct tables.
   1.321 +  const nsStyleDisplay* disp = StyleDisplay();
   1.322 +  if (disp->mDisplay == NS_STYLE_DISPLAY_TABLE) {
   1.323 +    // -moz-center and -moz-right are used for HTML's alignment
   1.324 +    // This is covering the <div align="right"><table>...</table></div> case.
   1.325 +    // In this case, we don't want to inherit the text alignment into the table.
   1.326 +    const nsStyleText* text = StyleText();
   1.327 +    
   1.328 +    if (text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER ||
   1.329 +        text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)
   1.330 +    {
   1.331 +      nsStyleText* uniqueText = (nsStyleText*)GetUniqueStyleData(eStyleStruct_Text);
   1.332 +      uniqueText->mTextAlign = NS_STYLE_TEXT_ALIGN_DEFAULT;
   1.333 +    }
   1.334 +  }
   1.335 +
   1.336 +  // CSS2.1 section 9.2.4 specifies fixups for the 'display' property of
   1.337 +  // the root element.  We can't implement them in nsRuleNode because we
   1.338 +  // don't want to store all display structs that aren't 'block',
   1.339 +  // 'inline', or 'table' in the style context tree on the off chance
   1.340 +  // that the root element has its style reresolved later.  So do them
   1.341 +  // here if needed, by changing the style data, so that other code
   1.342 +  // doesn't get confused by looking at the style data.
   1.343 +  if (!mParent) {
   1.344 +    uint8_t displayVal = disp->mDisplay;
   1.345 +    nsRuleNode::EnsureBlockDisplay(displayVal, true);
   1.346 +    if (displayVal != disp->mDisplay) {
   1.347 +      nsStyleDisplay *mutable_display =
   1.348 +        static_cast<nsStyleDisplay*>(GetUniqueStyleData(eStyleStruct_Display));
   1.349 +
   1.350 +      // If we're in this code, then mOriginalDisplay doesn't matter
   1.351 +      // for purposes of the cascade (because this nsStyleDisplay
   1.352 +      // isn't living in the ruletree anyway), and for determining
   1.353 +      // hypothetical boxes it's better to have mOriginalDisplay
   1.354 +      // matching mDisplay here.
   1.355 +      mutable_display->mOriginalDisplay = mutable_display->mDisplay =
   1.356 +        displayVal;
   1.357 +    }
   1.358 +  }
   1.359 +
   1.360 +  // Adjust the "display" values of flex and grid items (but not for raw text,
   1.361 +  // placeholders, or table-parts). CSS3 Flexbox section 4 says:
   1.362 +  //   # The computed 'display' of a flex item is determined
   1.363 +  //   # by applying the table in CSS 2.1 Chapter 9.7.
   1.364 +  // ...which converts inline-level elements to their block-level equivalents.
   1.365 +  if (!aSkipFlexItemStyleFixup && mParent) {
   1.366 +    const nsStyleDisplay* parentDisp = mParent->StyleDisplay();
   1.367 +    if ((parentDisp->mDisplay == NS_STYLE_DISPLAY_FLEX ||
   1.368 +         parentDisp->mDisplay == NS_STYLE_DISPLAY_INLINE_FLEX ||
   1.369 +         parentDisp->mDisplay == NS_STYLE_DISPLAY_GRID ||
   1.370 +         parentDisp->mDisplay == NS_STYLE_DISPLAY_INLINE_GRID) &&
   1.371 +        GetPseudo() != nsCSSAnonBoxes::mozNonElement) {
   1.372 +      uint8_t displayVal = disp->mDisplay;
   1.373 +      // Skip table parts.
   1.374 +      // NOTE: This list needs to be kept in sync with
   1.375 +      // nsCSSFrameConstructor.cpp's "sDisplayData" array -- specifically,
   1.376 +      // this should be the list of display-values that have
   1.377 +      // FCDATA_DESIRED_PARENT_TYPE_TO_BITS specified in that array.
   1.378 +      if (NS_STYLE_DISPLAY_TABLE_CAPTION      != displayVal &&
   1.379 +          NS_STYLE_DISPLAY_TABLE_ROW_GROUP    != displayVal &&
   1.380 +          NS_STYLE_DISPLAY_TABLE_HEADER_GROUP != displayVal &&
   1.381 +          NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP != displayVal &&
   1.382 +          NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP != displayVal &&
   1.383 +          NS_STYLE_DISPLAY_TABLE_COLUMN       != displayVal &&
   1.384 +          NS_STYLE_DISPLAY_TABLE_ROW          != displayVal &&
   1.385 +          NS_STYLE_DISPLAY_TABLE_CELL         != displayVal) {
   1.386 +
   1.387 +        // NOTE: Technically, we shouldn't modify the 'display' value of
   1.388 +        // positioned elements, since they aren't flex items. However, we don't
   1.389 +        // need to worry about checking for that, because if we're positioned,
   1.390 +        // we'll have already been through a call to EnsureBlockDisplay() in
   1.391 +        // nsRuleNode, so this call here won't change anything. So we're OK.
   1.392 +        nsRuleNode::EnsureBlockDisplay(displayVal);
   1.393 +        if (displayVal != disp->mDisplay) {
   1.394 +          NS_ASSERTION(!disp->IsAbsolutelyPositionedStyle(),
   1.395 +                       "We shouldn't be changing the display value of "
   1.396 +                       "positioned content (and we should have already "
   1.397 +                       "converted its display value to be block-level...)");
   1.398 +          nsStyleDisplay *mutable_display =
   1.399 +            static_cast<nsStyleDisplay*>(GetUniqueStyleData(eStyleStruct_Display));
   1.400 +          mutable_display->mDisplay = displayVal;
   1.401 +        }
   1.402 +      }
   1.403 +    }
   1.404 +  }
   1.405 +
   1.406 +  // Compute User Interface style, to trigger loads of cursors
   1.407 +  StyleUserInterface();
   1.408 +}
   1.409 +
   1.410 +nsChangeHint
   1.411 +nsStyleContext::CalcStyleDifference(nsStyleContext* aOther,
   1.412 +                                    nsChangeHint aParentHintsNotHandledForDescendants)
   1.413 +{
   1.414 +  PROFILER_LABEL("nsStyleContext", "CalcStyleDifference");
   1.415 +
   1.416 +  NS_ABORT_IF_FALSE(NS_IsHintSubset(aParentHintsNotHandledForDescendants,
   1.417 +                                    nsChangeHint_Hints_NotHandledForDescendants),
   1.418 +                    "caller is passing inherited hints, but shouldn't be");
   1.419 +
   1.420 +  nsChangeHint hint = NS_STYLE_HINT_NONE;
   1.421 +  NS_ENSURE_TRUE(aOther, hint);
   1.422 +  // We must always ensure that we populate the structs on the new style
   1.423 +  // context that are filled in on the old context, so that if we get
   1.424 +  // two style changes in succession, the second of which causes a real
   1.425 +  // style change, the PeekStyleData doesn't return null (implying that
   1.426 +  // nobody ever looked at that struct's data).  In other words, we
   1.427 +  // can't skip later structs if we get a big change up front, because
   1.428 +  // we could later get a small change in one of those structs that we
   1.429 +  // don't want to miss.
   1.430 +
   1.431 +  // If our rule nodes are the same, then any differences in style data
   1.432 +  // are already accounted for by differences on ancestors.  We know
   1.433 +  // this because CalcStyleDifference is always called on two style
   1.434 +  // contexts that point to the same element, so we know that our
   1.435 +  // position in the style context tree is the same and our position in
   1.436 +  // the rule node tree is also the same.
   1.437 +  // However, if there were noninherited style change hints on the
   1.438 +  // parent, we might produce these same noninherited hints on this
   1.439 +  // style context's frame due to 'inherit' values, so we do need to
   1.440 +  // compare.
   1.441 +  // (Things like 'em' units are handled by the change hint produced
   1.442 +  // by font-size changing, so we don't need to worry about them like
   1.443 +  // we worry about 'inherit' values.)
   1.444 +  bool compare = mRuleNode != aOther->mRuleNode;
   1.445 +
   1.446 +  // If we had any change in variable values, then we'll need to examine
   1.447 +  // all of the other style structs too, even if the new style context has
   1.448 +  // the same rule node as the old one.
   1.449 +  const nsStyleVariables* thisVariables = PeekStyleVariables();
   1.450 +  if (thisVariables) {
   1.451 +    const nsStyleVariables* otherVariables = aOther->StyleVariables();
   1.452 +    if (thisVariables->mVariables != otherVariables->mVariables) {
   1.453 +      compare = true;
   1.454 +    }
   1.455 +  }
   1.456 +
   1.457 +  DebugOnly<int> styleStructCount = 1;  // count Variables already
   1.458 +
   1.459 +#define DO_STRUCT_DIFFERENCE(struct_)                                         \
   1.460 +  PR_BEGIN_MACRO                                                              \
   1.461 +    const nsStyle##struct_* this##struct_ = PeekStyle##struct_();             \
   1.462 +    if (this##struct_) {                                                      \
   1.463 +      const nsStyle##struct_* other##struct_ = aOther->Style##struct_();      \
   1.464 +      nsChangeHint maxDifference = nsStyle##struct_::MaxDifference();         \
   1.465 +      nsChangeHint maxDifferenceNeverInherited =                              \
   1.466 +        nsStyle##struct_::MaxDifferenceNeverInherited();                      \
   1.467 +      if ((compare ||                                                         \
   1.468 +           (NS_SubtractHint(maxDifference, maxDifferenceNeverInherited) &     \
   1.469 +            aParentHintsNotHandledForDescendants)) &&                         \
   1.470 +          !NS_IsHintSubset(maxDifference, hint) &&                            \
   1.471 +          this##struct_ != other##struct_) {                                  \
   1.472 +        NS_ASSERTION(NS_IsHintSubset(                                         \
   1.473 +             this##struct_->CalcDifference(*other##struct_),                  \
   1.474 +             nsStyle##struct_::MaxDifference()),                              \
   1.475 +             "CalcDifference() returned bigger hint than MaxDifference()");   \
   1.476 +        NS_UpdateHint(hint, this##struct_->CalcDifference(*other##struct_));  \
   1.477 +      }                                                                       \
   1.478 +    }                                                                         \
   1.479 +    styleStructCount++;                                                       \
   1.480 +  PR_END_MACRO
   1.481 +
   1.482 +  // In general, we want to examine structs starting with those that can
   1.483 +  // cause the largest style change, down to those that can cause the
   1.484 +  // smallest.  This lets us skip later ones if we already have a hint
   1.485 +  // that subsumes their MaxDifference.  (As the hints get
   1.486 +  // finer-grained, this optimization is becoming less useful, though.)
   1.487 +  DO_STRUCT_DIFFERENCE(Display);
   1.488 +  DO_STRUCT_DIFFERENCE(XUL);
   1.489 +  DO_STRUCT_DIFFERENCE(Column);
   1.490 +  DO_STRUCT_DIFFERENCE(Content);
   1.491 +  DO_STRUCT_DIFFERENCE(UserInterface);
   1.492 +  DO_STRUCT_DIFFERENCE(Visibility);
   1.493 +  DO_STRUCT_DIFFERENCE(Outline);
   1.494 +  DO_STRUCT_DIFFERENCE(TableBorder);
   1.495 +  DO_STRUCT_DIFFERENCE(Table);
   1.496 +  DO_STRUCT_DIFFERENCE(UIReset);
   1.497 +  DO_STRUCT_DIFFERENCE(Text);
   1.498 +  DO_STRUCT_DIFFERENCE(List);
   1.499 +  DO_STRUCT_DIFFERENCE(Quotes);
   1.500 +  DO_STRUCT_DIFFERENCE(SVGReset);
   1.501 +  DO_STRUCT_DIFFERENCE(SVG);
   1.502 +  DO_STRUCT_DIFFERENCE(Position);
   1.503 +  DO_STRUCT_DIFFERENCE(Font);
   1.504 +  DO_STRUCT_DIFFERENCE(Margin);
   1.505 +  DO_STRUCT_DIFFERENCE(Padding);
   1.506 +  DO_STRUCT_DIFFERENCE(Border);
   1.507 +  DO_STRUCT_DIFFERENCE(TextReset);
   1.508 +  DO_STRUCT_DIFFERENCE(Background);
   1.509 +  DO_STRUCT_DIFFERENCE(Color);
   1.510 +
   1.511 +#undef DO_STRUCT_DIFFERENCE
   1.512 +
   1.513 +  MOZ_ASSERT(styleStructCount == nsStyleStructID_Length,
   1.514 +             "missing a call to DO_STRUCT_DIFFERENCE");
   1.515 +
   1.516 +  // Note that we do not check whether this->RelevantLinkVisited() !=
   1.517 +  // aOther->RelevantLinkVisited(); we don't need to since
   1.518 +  // nsCSSFrameConstructor::DoContentStateChanged always adds
   1.519 +  // nsChangeHint_RepaintFrame for NS_EVENT_STATE_VISITED changes (and
   1.520 +  // needs to, since HasStateDependentStyle probably doesn't work right
   1.521 +  // for NS_EVENT_STATE_VISITED).  Hopefully this doesn't actually
   1.522 +  // expose whether links are visited to performance tests since all
   1.523 +  // link coloring happens asynchronously at a time when it's hard for
   1.524 +  // the page to measure.
   1.525 +  // However, we do need to compute the larger of the changes that can
   1.526 +  // happen depending on whether the link is visited or unvisited, since
   1.527 +  // doing only the one that's currently appropriate would expose which
   1.528 +  // links are in history to easy performance measurement.  Therefore,
   1.529 +  // here, we add nsChangeHint_RepaintFrame hints (the maximum for
   1.530 +  // things that can depend on :visited) for the properties on which we
   1.531 +  // call GetVisitedDependentColor.
   1.532 +  nsStyleContext *thisVis = GetStyleIfVisited(),
   1.533 +                *otherVis = aOther->GetStyleIfVisited();
   1.534 +  if (!thisVis != !otherVis) {
   1.535 +    // One style context has a style-if-visited and the other doesn't.
   1.536 +    // Presume a difference.
   1.537 +    NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
   1.538 +  } else if (thisVis && !NS_IsHintSubset(nsChangeHint_RepaintFrame, hint)) {
   1.539 +    // Both style contexts have a style-if-visited.
   1.540 +    bool change = false;
   1.541 +
   1.542 +    // NB: Calling Peek on |this|, not |thisVis|, since callers may look
   1.543 +    // at a struct on |this| without looking at the same struct on
   1.544 +    // |thisVis| (including this function if we skip one of these checks
   1.545 +    // due to change being true already or due to the old style context
   1.546 +    // not having a style-if-visited), but not the other way around.
   1.547 +    if (PeekStyleColor()) {
   1.548 +      if (thisVis->StyleColor()->mColor !=
   1.549 +          otherVis->StyleColor()->mColor) {
   1.550 +        change = true;
   1.551 +      }
   1.552 +    }
   1.553 +
   1.554 +    // NB: Calling Peek on |this|, not |thisVis| (see above).
   1.555 +    if (!change && PeekStyleBackground()) {
   1.556 +      if (thisVis->StyleBackground()->mBackgroundColor !=
   1.557 +          otherVis->StyleBackground()->mBackgroundColor) {
   1.558 +        change = true;
   1.559 +      }
   1.560 +    }
   1.561 +
   1.562 +    // NB: Calling Peek on |this|, not |thisVis| (see above).
   1.563 +    if (!change && PeekStyleBorder()) {
   1.564 +      const nsStyleBorder *thisVisBorder = thisVis->StyleBorder();
   1.565 +      const nsStyleBorder *otherVisBorder = otherVis->StyleBorder();
   1.566 +      NS_FOR_CSS_SIDES(side) {
   1.567 +        bool thisFG, otherFG;
   1.568 +        nscolor thisColor, otherColor;
   1.569 +        thisVisBorder->GetBorderColor(side, thisColor, thisFG);
   1.570 +        otherVisBorder->GetBorderColor(side, otherColor, otherFG);
   1.571 +        if (thisFG != otherFG || (!thisFG && thisColor != otherColor)) {
   1.572 +          change = true;
   1.573 +          break;
   1.574 +        }
   1.575 +      }
   1.576 +    }
   1.577 +
   1.578 +    // NB: Calling Peek on |this|, not |thisVis| (see above).
   1.579 +    if (!change && PeekStyleOutline()) {
   1.580 +      const nsStyleOutline *thisVisOutline = thisVis->StyleOutline();
   1.581 +      const nsStyleOutline *otherVisOutline = otherVis->StyleOutline();
   1.582 +      bool haveColor;
   1.583 +      nscolor thisColor, otherColor;
   1.584 +      if (thisVisOutline->GetOutlineInitialColor() != 
   1.585 +            otherVisOutline->GetOutlineInitialColor() ||
   1.586 +          (haveColor = thisVisOutline->GetOutlineColor(thisColor)) != 
   1.587 +            otherVisOutline->GetOutlineColor(otherColor) ||
   1.588 +          (haveColor && thisColor != otherColor)) {
   1.589 +        change = true;
   1.590 +      }
   1.591 +    }
   1.592 +
   1.593 +    // NB: Calling Peek on |this|, not |thisVis| (see above).
   1.594 +    if (!change && PeekStyleColumn()) {
   1.595 +      const nsStyleColumn *thisVisColumn = thisVis->StyleColumn();
   1.596 +      const nsStyleColumn *otherVisColumn = otherVis->StyleColumn();
   1.597 +      if (thisVisColumn->mColumnRuleColor != otherVisColumn->mColumnRuleColor ||
   1.598 +          thisVisColumn->mColumnRuleColorIsForeground !=
   1.599 +            otherVisColumn->mColumnRuleColorIsForeground) {
   1.600 +        change = true;
   1.601 +      }
   1.602 +    }
   1.603 +
   1.604 +    // NB: Calling Peek on |this|, not |thisVis| (see above).
   1.605 +    if (!change && PeekStyleTextReset()) {
   1.606 +      const nsStyleTextReset *thisVisTextReset = thisVis->StyleTextReset();
   1.607 +      const nsStyleTextReset *otherVisTextReset = otherVis->StyleTextReset();
   1.608 +      nscolor thisVisDecColor, otherVisDecColor;
   1.609 +      bool thisVisDecColorIsFG, otherVisDecColorIsFG;
   1.610 +      thisVisTextReset->GetDecorationColor(thisVisDecColor,
   1.611 +                                           thisVisDecColorIsFG);
   1.612 +      otherVisTextReset->GetDecorationColor(otherVisDecColor,
   1.613 +                                            otherVisDecColorIsFG);
   1.614 +      if (thisVisDecColorIsFG != otherVisDecColorIsFG ||
   1.615 +          (!thisVisDecColorIsFG && thisVisDecColor != otherVisDecColor)) {
   1.616 +        change = true;
   1.617 +      }
   1.618 +    }
   1.619 +
   1.620 +    // NB: Calling Peek on |this|, not |thisVis| (see above).
   1.621 +    if (!change && PeekStyleSVG()) {
   1.622 +      const nsStyleSVG *thisVisSVG = thisVis->StyleSVG();
   1.623 +      const nsStyleSVG *otherVisSVG = otherVis->StyleSVG();
   1.624 +      if (thisVisSVG->mFill != otherVisSVG->mFill ||
   1.625 +          thisVisSVG->mStroke != otherVisSVG->mStroke) {
   1.626 +        change = true;
   1.627 +      }
   1.628 +    }
   1.629 +
   1.630 +    if (change) {
   1.631 +      NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
   1.632 +    }
   1.633 +  }
   1.634 +
   1.635 +  return hint;
   1.636 +}
   1.637 +
   1.638 +void
   1.639 +nsStyleContext::Mark()
   1.640 +{
   1.641 +  // Mark our rule node.
   1.642 +  mRuleNode->Mark();
   1.643 +
   1.644 +  // Mark our children (i.e., tell them to mark their rule nodes, etc.).
   1.645 +  if (mChild) {
   1.646 +    nsStyleContext* child = mChild;
   1.647 +    do {
   1.648 +      child->Mark();
   1.649 +      child = child->mNextSibling;
   1.650 +    } while (mChild != child);
   1.651 +  }
   1.652 +  
   1.653 +  if (mEmptyChild) {
   1.654 +    nsStyleContext* child = mEmptyChild;
   1.655 +    do {
   1.656 +      child->Mark();
   1.657 +      child = child->mNextSibling;
   1.658 +    } while (mEmptyChild != child);
   1.659 +  }
   1.660 +}
   1.661 +
   1.662 +#ifdef DEBUG
   1.663 +void nsStyleContext::List(FILE* out, int32_t aIndent)
   1.664 +{
   1.665 +  // Indent
   1.666 +  int32_t ix;
   1.667 +  for (ix = aIndent; --ix >= 0; ) fputs("  ", out);
   1.668 +  fprintf(out, "%p(%d) parent=%p ",
   1.669 +          (void*)this, mRefCnt, (void *)mParent);
   1.670 +  if (mPseudoTag) {
   1.671 +    nsAutoString  buffer;
   1.672 +    mPseudoTag->ToString(buffer);
   1.673 +    fputs(NS_LossyConvertUTF16toASCII(buffer).get(), out);
   1.674 +    fputs(" ", out);
   1.675 +  }
   1.676 +
   1.677 +  if (mRuleNode) {
   1.678 +    fputs("{\n", out);
   1.679 +    nsRuleNode* ruleNode = mRuleNode;
   1.680 +    while (ruleNode) {
   1.681 +      nsIStyleRule *styleRule = ruleNode->GetRule();
   1.682 +      if (styleRule) {
   1.683 +        styleRule->List(out, aIndent + 1);
   1.684 +      }
   1.685 +      ruleNode = ruleNode->GetParent();
   1.686 +    }
   1.687 +    for (ix = aIndent; --ix >= 0; ) fputs("  ", out);
   1.688 +    fputs("}\n", out);
   1.689 +  }
   1.690 +  else {
   1.691 +    fputs("{}\n", out);
   1.692 +  }
   1.693 +
   1.694 +  if (nullptr != mChild) {
   1.695 +    nsStyleContext* child = mChild;
   1.696 +    do {
   1.697 +      child->List(out, aIndent + 1);
   1.698 +      child = child->mNextSibling;
   1.699 +    } while (mChild != child);
   1.700 +  }
   1.701 +  if (nullptr != mEmptyChild) {
   1.702 +    nsStyleContext* child = mEmptyChild;
   1.703 +    do {
   1.704 +      child->List(out, aIndent + 1);
   1.705 +      child = child->mNextSibling;
   1.706 +    } while (mEmptyChild != child);
   1.707 +  }
   1.708 +}
   1.709 +#endif
   1.710 +
   1.711 +// Overloaded new operator. Initializes the memory to 0 and relies on an arena
   1.712 +// (which comes from the presShell) to perform the allocation.
   1.713 +void* 
   1.714 +nsStyleContext::operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW
   1.715 +{
   1.716 +  // Check the recycle list first.
   1.717 +  return aPresContext->PresShell()->AllocateByObjectID(nsPresArena::nsStyleContext_id, sz);
   1.718 +}
   1.719 +
   1.720 +// Overridden to prevent the global delete from being called, since the memory
   1.721 +// came out of an nsIArena instead of the global delete operator's heap.
   1.722 +void 
   1.723 +nsStyleContext::Destroy()
   1.724 +{
   1.725 +  // Get the pres context from our rule node.
   1.726 +  nsRefPtr<nsPresContext> presContext = mRuleNode->PresContext();
   1.727 +
   1.728 +  // Call our destructor.
   1.729 +  this->~nsStyleContext();
   1.730 +
   1.731 +  // Don't let the memory be freed, since it will be recycled
   1.732 +  // instead. Don't call the global operator delete.
   1.733 +  presContext->PresShell()->FreeByObjectID(nsPresArena::nsStyleContext_id, this);
   1.734 +}
   1.735 +
   1.736 +already_AddRefed<nsStyleContext>
   1.737 +NS_NewStyleContext(nsStyleContext* aParentContext,
   1.738 +                   nsIAtom* aPseudoTag,
   1.739 +                   nsCSSPseudoElements::Type aPseudoType,
   1.740 +                   nsRuleNode* aRuleNode,
   1.741 +                   bool aSkipFlexItemStyleFixup)
   1.742 +{
   1.743 +  nsRefPtr<nsStyleContext> context =
   1.744 +    new (aRuleNode->PresContext())
   1.745 +    nsStyleContext(aParentContext, aPseudoTag, aPseudoType, aRuleNode,
   1.746 +                   aSkipFlexItemStyleFixup);
   1.747 +  return context.forget();
   1.748 +}
   1.749 +
   1.750 +static inline void
   1.751 +ExtractAnimationValue(nsCSSProperty aProperty,
   1.752 +                      nsStyleContext* aStyleContext,
   1.753 +                      nsStyleAnimation::Value& aResult)
   1.754 +{
   1.755 +  DebugOnly<bool> success =
   1.756 +    nsStyleAnimation::ExtractComputedValue(aProperty, aStyleContext, aResult);
   1.757 +  NS_ABORT_IF_FALSE(success,
   1.758 +                    "aProperty must be extractable by nsStyleAnimation");
   1.759 +}
   1.760 +
   1.761 +static nscolor
   1.762 +ExtractColor(nsCSSProperty aProperty,
   1.763 +             nsStyleContext *aStyleContext)
   1.764 +{
   1.765 +  nsStyleAnimation::Value val;
   1.766 +  ExtractAnimationValue(aProperty, aStyleContext, val);
   1.767 +  return val.GetColorValue();
   1.768 +}
   1.769 +
   1.770 +static nscolor
   1.771 +ExtractColorLenient(nsCSSProperty aProperty,
   1.772 +                    nsStyleContext *aStyleContext)
   1.773 +{
   1.774 +  nsStyleAnimation::Value val;
   1.775 +  ExtractAnimationValue(aProperty, aStyleContext, val);
   1.776 +  if (val.GetUnit() == nsStyleAnimation::eUnit_Color) {
   1.777 +    return val.GetColorValue();
   1.778 +  }
   1.779 +  return NS_RGBA(0, 0, 0, 0);
   1.780 +}
   1.781 +
   1.782 +struct ColorIndexSet {
   1.783 +  uint8_t colorIndex, alphaIndex;
   1.784 +};
   1.785 +
   1.786 +static const ColorIndexSet gVisitedIndices[2] = { { 0, 0 }, { 1, 0 } };
   1.787 +
   1.788 +nscolor
   1.789 +nsStyleContext::GetVisitedDependentColor(nsCSSProperty aProperty)
   1.790 +{
   1.791 +  NS_ASSERTION(aProperty == eCSSProperty_color ||
   1.792 +               aProperty == eCSSProperty_background_color ||
   1.793 +               aProperty == eCSSProperty_border_top_color ||
   1.794 +               aProperty == eCSSProperty_border_right_color_value ||
   1.795 +               aProperty == eCSSProperty_border_bottom_color ||
   1.796 +               aProperty == eCSSProperty_border_left_color_value ||
   1.797 +               aProperty == eCSSProperty_outline_color ||
   1.798 +               aProperty == eCSSProperty__moz_column_rule_color ||
   1.799 +               aProperty == eCSSProperty_text_decoration_color ||
   1.800 +               aProperty == eCSSProperty_fill ||
   1.801 +               aProperty == eCSSProperty_stroke,
   1.802 +               "we need to add to nsStyleContext::CalcStyleDifference");
   1.803 +
   1.804 +  bool isPaintProperty = aProperty == eCSSProperty_fill ||
   1.805 +                         aProperty == eCSSProperty_stroke;
   1.806 +
   1.807 +  nscolor colors[2];
   1.808 +  colors[0] = isPaintProperty ? ExtractColorLenient(aProperty, this)
   1.809 +                              : ExtractColor(aProperty, this);
   1.810 +
   1.811 +  nsStyleContext *visitedStyle = this->GetStyleIfVisited();
   1.812 +  if (!visitedStyle) {
   1.813 +    return colors[0];
   1.814 +  }
   1.815 +
   1.816 +  colors[1] = isPaintProperty ? ExtractColorLenient(aProperty, visitedStyle)
   1.817 +                              : ExtractColor(aProperty, visitedStyle);
   1.818 +
   1.819 +  return nsStyleContext::CombineVisitedColors(colors,
   1.820 +                                              this->RelevantLinkVisited());
   1.821 +}
   1.822 +
   1.823 +/* static */ nscolor
   1.824 +nsStyleContext::CombineVisitedColors(nscolor *aColors, bool aLinkIsVisited)
   1.825 +{
   1.826 +  if (NS_GET_A(aColors[1]) == 0) {
   1.827 +    // If the style-if-visited is transparent, then just use the
   1.828 +    // unvisited style rather than using the (meaningless) color
   1.829 +    // components of the visited style along with a potentially
   1.830 +    // non-transparent alpha value.
   1.831 +    aLinkIsVisited = false;
   1.832 +  }
   1.833 +
   1.834 +  // NOTE: We want this code to have as little timing dependence as
   1.835 +  // possible on whether this->RelevantLinkVisited() is true.
   1.836 +  const ColorIndexSet &set =
   1.837 +    gVisitedIndices[aLinkIsVisited ? 1 : 0];
   1.838 +
   1.839 +  nscolor colorColor = aColors[set.colorIndex];
   1.840 +  nscolor alphaColor = aColors[set.alphaIndex];
   1.841 +  return NS_RGBA(NS_GET_R(colorColor), NS_GET_G(colorColor),
   1.842 +                 NS_GET_B(colorColor), NS_GET_A(alphaColor));
   1.843 +}
   1.844 +
   1.845 +void*
   1.846 +nsStyleContext::Alloc(size_t aSize)
   1.847 +{
   1.848 +  nsIPresShell *shell = PresContext()->PresShell();
   1.849 +
   1.850 +  aSize += offsetof(AllocationHeader, mStorageStart);
   1.851 +  AllocationHeader *alloc =
   1.852 +    static_cast<AllocationHeader*>(shell->AllocateMisc(aSize));
   1.853 +
   1.854 +  alloc->mSize = aSize; // NOTE: inflated by header
   1.855 +
   1.856 +  alloc->mNext = mAllocations;
   1.857 +  mAllocations = alloc;
   1.858 +
   1.859 +  return static_cast<void*>(&alloc->mStorageStart);
   1.860 +}
   1.861 +
   1.862 +void
   1.863 +nsStyleContext::FreeAllocations(nsPresContext *aPresContext)
   1.864 +{
   1.865 +  nsIPresShell *shell = aPresContext->PresShell();
   1.866 +
   1.867 +  for (AllocationHeader *alloc = mAllocations, *next; alloc; alloc = next) {
   1.868 +    next = alloc->mNext;
   1.869 +    shell->FreeMisc(alloc->mSize, alloc);
   1.870 +  }
   1.871 +}
   1.872 +
   1.873 +#ifdef DEBUG
   1.874 +/* static */ void
   1.875 +nsStyleContext::AssertStyleStructMaxDifferenceValid()
   1.876 +{
   1.877 +#define STYLE_STRUCT(name, checkdata_cb)                                     \
   1.878 +    MOZ_ASSERT(NS_IsHintSubset(nsStyle##name::MaxDifferenceNeverInherited(), \
   1.879 +                               nsStyle##name::MaxDifference()));
   1.880 +#include "nsStyleStructList.h"
   1.881 +#undef STYLE_STRUCT
   1.882 +}
   1.883 +#endif

mercurial