layout/style/nsStyleContext.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 /* the interface (to internal code) for retrieving computed style data */
michael@0 7
michael@0 8 #include "mozilla/DebugOnly.h"
michael@0 9
michael@0 10 #include "nsCSSAnonBoxes.h"
michael@0 11 #include "nsStyleConsts.h"
michael@0 12 #include "nsString.h"
michael@0 13 #include "nsPresContext.h"
michael@0 14 #include "nsIStyleRule.h"
michael@0 15
michael@0 16 #include "nsCOMPtr.h"
michael@0 17 #include "nsStyleSet.h"
michael@0 18 #include "nsIPresShell.h"
michael@0 19
michael@0 20 #include "nsRuleNode.h"
michael@0 21 #include "nsStyleContext.h"
michael@0 22 #include "nsStyleAnimation.h"
michael@0 23 #include "GeckoProfiler.h"
michael@0 24
michael@0 25 #ifdef DEBUG
michael@0 26 // #define NOISY_DEBUG
michael@0 27 #endif
michael@0 28
michael@0 29 using namespace mozilla;
michael@0 30
michael@0 31 //----------------------------------------------------------------------
michael@0 32
michael@0 33
michael@0 34 nsStyleContext::nsStyleContext(nsStyleContext* aParent,
michael@0 35 nsIAtom* aPseudoTag,
michael@0 36 nsCSSPseudoElements::Type aPseudoType,
michael@0 37 nsRuleNode* aRuleNode,
michael@0 38 bool aSkipFlexItemStyleFixup)
michael@0 39 : mParent(aParent),
michael@0 40 mChild(nullptr),
michael@0 41 mEmptyChild(nullptr),
michael@0 42 mPseudoTag(aPseudoTag),
michael@0 43 mRuleNode(aRuleNode),
michael@0 44 mAllocations(nullptr),
michael@0 45 mCachedResetData(nullptr),
michael@0 46 mBits(((uint64_t)aPseudoType) << NS_STYLE_CONTEXT_TYPE_SHIFT),
michael@0 47 mRefCnt(0)
michael@0 48 {
michael@0 49 // This check has to be done "backward", because if it were written the
michael@0 50 // more natural way it wouldn't fail even when it needed to.
michael@0 51 static_assert((UINT64_MAX >> NS_STYLE_CONTEXT_TYPE_SHIFT) >=
michael@0 52 nsCSSPseudoElements::ePseudo_MAX,
michael@0 53 "pseudo element bits no longer fit in a uint64_t");
michael@0 54 MOZ_ASSERT(aRuleNode);
michael@0 55
michael@0 56 mNextSibling = this;
michael@0 57 mPrevSibling = this;
michael@0 58 if (mParent) {
michael@0 59 mParent->AddRef();
michael@0 60 mParent->AddChild(this);
michael@0 61 #ifdef DEBUG
michael@0 62 nsRuleNode *r1 = mParent->RuleNode(), *r2 = aRuleNode;
michael@0 63 while (r1->GetParent())
michael@0 64 r1 = r1->GetParent();
michael@0 65 while (r2->GetParent())
michael@0 66 r2 = r2->GetParent();
michael@0 67 NS_ASSERTION(r1 == r2, "must be in the same rule tree as parent");
michael@0 68 #endif
michael@0 69 }
michael@0 70
michael@0 71 mRuleNode->AddRef();
michael@0 72 mRuleNode->SetUsedDirectly(); // before ApplyStyleFixups()!
michael@0 73
michael@0 74 ApplyStyleFixups(aSkipFlexItemStyleFixup);
michael@0 75
michael@0 76 #define eStyleStruct_LastItem (nsStyleStructID_Length - 1)
michael@0 77 NS_ASSERTION(NS_STYLE_INHERIT_MASK & NS_STYLE_INHERIT_BIT(LastItem),
michael@0 78 "NS_STYLE_INHERIT_MASK must be bigger, and other bits shifted");
michael@0 79 #undef eStyleStruct_LastItem
michael@0 80 }
michael@0 81
michael@0 82 nsStyleContext::~nsStyleContext()
michael@0 83 {
michael@0 84 NS_ASSERTION((nullptr == mChild) && (nullptr == mEmptyChild), "destructing context with children");
michael@0 85
michael@0 86 nsPresContext *presContext = mRuleNode->PresContext();
michael@0 87
michael@0 88 mRuleNode->Release();
michael@0 89
michael@0 90 presContext->PresShell()->StyleSet()->
michael@0 91 NotifyStyleContextDestroyed(presContext, this);
michael@0 92
michael@0 93 if (mParent) {
michael@0 94 mParent->RemoveChild(this);
michael@0 95 mParent->Release();
michael@0 96 }
michael@0 97
michael@0 98 // Free up our data structs.
michael@0 99 mCachedInheritedData.DestroyStructs(mBits, presContext);
michael@0 100 if (mCachedResetData) {
michael@0 101 mCachedResetData->Destroy(mBits, presContext);
michael@0 102 }
michael@0 103
michael@0 104 FreeAllocations(presContext);
michael@0 105 }
michael@0 106
michael@0 107 void nsStyleContext::AddChild(nsStyleContext* aChild)
michael@0 108 {
michael@0 109 NS_ASSERTION(aChild->mPrevSibling == aChild &&
michael@0 110 aChild->mNextSibling == aChild,
michael@0 111 "child already in a child list");
michael@0 112
michael@0 113 nsStyleContext **listPtr = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild;
michael@0 114 // Explicitly dereference listPtr so that compiler doesn't have to know that mNextSibling
michael@0 115 // etc. don't alias with what ever listPtr points at.
michael@0 116 nsStyleContext *list = *listPtr;
michael@0 117
michael@0 118 // Insert at the beginning of the list. See also FindChildWithRules.
michael@0 119 if (list) {
michael@0 120 // Link into existing elements, if there are any.
michael@0 121 aChild->mNextSibling = list;
michael@0 122 aChild->mPrevSibling = list->mPrevSibling;
michael@0 123 list->mPrevSibling->mNextSibling = aChild;
michael@0 124 list->mPrevSibling = aChild;
michael@0 125 }
michael@0 126 (*listPtr) = aChild;
michael@0 127 }
michael@0 128
michael@0 129 void nsStyleContext::RemoveChild(nsStyleContext* aChild)
michael@0 130 {
michael@0 131 NS_PRECONDITION(nullptr != aChild && this == aChild->mParent, "bad argument");
michael@0 132
michael@0 133 nsStyleContext **list = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild;
michael@0 134
michael@0 135 if (aChild->mPrevSibling != aChild) { // has siblings
michael@0 136 if ((*list) == aChild) {
michael@0 137 (*list) = (*list)->mNextSibling;
michael@0 138 }
michael@0 139 }
michael@0 140 else {
michael@0 141 NS_ASSERTION((*list) == aChild, "bad sibling pointers");
michael@0 142 (*list) = nullptr;
michael@0 143 }
michael@0 144
michael@0 145 aChild->mPrevSibling->mNextSibling = aChild->mNextSibling;
michael@0 146 aChild->mNextSibling->mPrevSibling = aChild->mPrevSibling;
michael@0 147 aChild->mNextSibling = aChild;
michael@0 148 aChild->mPrevSibling = aChild;
michael@0 149 }
michael@0 150
michael@0 151 already_AddRefed<nsStyleContext>
michael@0 152 nsStyleContext::FindChildWithRules(const nsIAtom* aPseudoTag,
michael@0 153 nsRuleNode* aRuleNode,
michael@0 154 nsRuleNode* aRulesIfVisited,
michael@0 155 bool aRelevantLinkVisited)
michael@0 156 {
michael@0 157 NS_ABORT_IF_FALSE(aRulesIfVisited || !aRelevantLinkVisited,
michael@0 158 "aRelevantLinkVisited should only be set when we have a separate style");
michael@0 159 uint32_t threshold = 10; // The # of siblings we're willing to examine
michael@0 160 // before just giving this whole thing up.
michael@0 161
michael@0 162 nsRefPtr<nsStyleContext> result;
michael@0 163 nsStyleContext *list = aRuleNode->IsRoot() ? mEmptyChild : mChild;
michael@0 164
michael@0 165 if (list) {
michael@0 166 nsStyleContext *child = list;
michael@0 167 do {
michael@0 168 if (child->mRuleNode == aRuleNode &&
michael@0 169 child->mPseudoTag == aPseudoTag &&
michael@0 170 !child->IsStyleIfVisited() &&
michael@0 171 child->RelevantLinkVisited() == aRelevantLinkVisited) {
michael@0 172 bool match = false;
michael@0 173 if (aRulesIfVisited) {
michael@0 174 match = child->GetStyleIfVisited() &&
michael@0 175 child->GetStyleIfVisited()->mRuleNode == aRulesIfVisited;
michael@0 176 } else {
michael@0 177 match = !child->GetStyleIfVisited();
michael@0 178 }
michael@0 179 if (match) {
michael@0 180 result = child;
michael@0 181 break;
michael@0 182 }
michael@0 183 }
michael@0 184 child = child->mNextSibling;
michael@0 185 threshold--;
michael@0 186 if (threshold == 0)
michael@0 187 break;
michael@0 188 } while (child != list);
michael@0 189 }
michael@0 190
michael@0 191 if (result) {
michael@0 192 if (result != list) {
michael@0 193 // Move result to the front of the list.
michael@0 194 RemoveChild(result);
michael@0 195 AddChild(result);
michael@0 196 }
michael@0 197 }
michael@0 198
michael@0 199 return result.forget();
michael@0 200 }
michael@0 201
michael@0 202 const void* nsStyleContext::GetCachedStyleData(nsStyleStructID aSID)
michael@0 203 {
michael@0 204 const void* cachedData;
michael@0 205 if (nsCachedStyleData::IsReset(aSID)) {
michael@0 206 if (mCachedResetData) {
michael@0 207 cachedData = mCachedResetData->mStyleStructs[aSID];
michael@0 208 } else {
michael@0 209 cachedData = nullptr;
michael@0 210 }
michael@0 211 } else {
michael@0 212 cachedData = mCachedInheritedData.mStyleStructs[aSID];
michael@0 213 }
michael@0 214 return cachedData;
michael@0 215 }
michael@0 216
michael@0 217 const void* nsStyleContext::StyleData(nsStyleStructID aSID)
michael@0 218 {
michael@0 219 const void* cachedData = GetCachedStyleData(aSID);
michael@0 220 if (cachedData)
michael@0 221 return cachedData; // We have computed data stored on this node in the context tree.
michael@0 222 return mRuleNode->GetStyleData(aSID, this, true); // Our rule node will take care of it for us.
michael@0 223 }
michael@0 224
michael@0 225 // This is an evil evil function, since it forces you to alloc your own separate copy of
michael@0 226 // style data! Do not use this function unless you absolutely have to! You should avoid
michael@0 227 // this at all costs! -dwh
michael@0 228 void*
michael@0 229 nsStyleContext::GetUniqueStyleData(const nsStyleStructID& aSID)
michael@0 230 {
michael@0 231 // If we already own the struct and no kids could depend on it, then
michael@0 232 // just return it. (We leak in this case if there are kids -- and this
michael@0 233 // function really shouldn't be called for style contexts that could
michael@0 234 // have kids depending on the data. ClearStyleData would be OK, but
michael@0 235 // this test for no mChild or mEmptyChild doesn't catch that case.)
michael@0 236 const void *current = StyleData(aSID);
michael@0 237 if (!mChild && !mEmptyChild &&
michael@0 238 !(mBits & nsCachedStyleData::GetBitForSID(aSID)) &&
michael@0 239 GetCachedStyleData(aSID))
michael@0 240 return const_cast<void*>(current);
michael@0 241
michael@0 242 void* result;
michael@0 243 nsPresContext *presContext = PresContext();
michael@0 244 switch (aSID) {
michael@0 245
michael@0 246 #define UNIQUE_CASE(c_) \
michael@0 247 case eStyleStruct_##c_: \
michael@0 248 result = new (presContext) nsStyle##c_( \
michael@0 249 * static_cast<const nsStyle##c_ *>(current)); \
michael@0 250 break;
michael@0 251
michael@0 252 UNIQUE_CASE(Display)
michael@0 253 UNIQUE_CASE(Background)
michael@0 254 UNIQUE_CASE(Text)
michael@0 255 UNIQUE_CASE(TextReset)
michael@0 256
michael@0 257 #undef UNIQUE_CASE
michael@0 258
michael@0 259 default:
michael@0 260 NS_ERROR("Struct type not supported. Please find another way to do this if you can!");
michael@0 261 return nullptr;
michael@0 262 }
michael@0 263
michael@0 264 SetStyle(aSID, result);
michael@0 265 mBits &= ~static_cast<uint64_t>(nsCachedStyleData::GetBitForSID(aSID));
michael@0 266
michael@0 267 return result;
michael@0 268 }
michael@0 269
michael@0 270 void
michael@0 271 nsStyleContext::SetStyle(nsStyleStructID aSID, void* aStruct)
michael@0 272 {
michael@0 273 // This method should only be called from nsRuleNode! It is not a public
michael@0 274 // method!
michael@0 275
michael@0 276 NS_ASSERTION(aSID >= 0 && aSID < nsStyleStructID_Length, "out of bounds");
michael@0 277
michael@0 278 // NOTE: nsCachedStyleData::GetStyleData works roughly the same way.
michael@0 279 // See the comments there (in nsRuleNode.h) for more details about
michael@0 280 // what this is doing and why.
michael@0 281
michael@0 282 void** dataSlot;
michael@0 283 if (nsCachedStyleData::IsReset(aSID)) {
michael@0 284 if (!mCachedResetData) {
michael@0 285 mCachedResetData = new (mRuleNode->PresContext()) nsResetStyleData;
michael@0 286 }
michael@0 287 dataSlot = &mCachedResetData->mStyleStructs[aSID];
michael@0 288 } else {
michael@0 289 dataSlot = &mCachedInheritedData.mStyleStructs[aSID];
michael@0 290 }
michael@0 291 NS_ASSERTION(!*dataSlot || (mBits & nsCachedStyleData::GetBitForSID(aSID)),
michael@0 292 "Going to leak style data");
michael@0 293 *dataSlot = aStruct;
michael@0 294 }
michael@0 295
michael@0 296 void
michael@0 297 nsStyleContext::ApplyStyleFixups(bool aSkipFlexItemStyleFixup)
michael@0 298 {
michael@0 299 // See if we have any text decorations.
michael@0 300 // First see if our parent has text decorations. If our parent does, then we inherit the bit.
michael@0 301 if (mParent && mParent->HasTextDecorationLines()) {
michael@0 302 mBits |= NS_STYLE_HAS_TEXT_DECORATION_LINES;
michael@0 303 } else {
michael@0 304 // We might have defined a decoration.
michael@0 305 const nsStyleTextReset* text = StyleTextReset();
michael@0 306 uint8_t decorationLine = text->mTextDecorationLine;
michael@0 307 if (decorationLine != NS_STYLE_TEXT_DECORATION_LINE_NONE &&
michael@0 308 decorationLine != NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL) {
michael@0 309 mBits |= NS_STYLE_HAS_TEXT_DECORATION_LINES;
michael@0 310 }
michael@0 311 }
michael@0 312
michael@0 313 if ((mParent && mParent->HasPseudoElementData()) || mPseudoTag) {
michael@0 314 mBits |= NS_STYLE_HAS_PSEUDO_ELEMENT_DATA;
michael@0 315 }
michael@0 316
michael@0 317 // Correct tables.
michael@0 318 const nsStyleDisplay* disp = StyleDisplay();
michael@0 319 if (disp->mDisplay == NS_STYLE_DISPLAY_TABLE) {
michael@0 320 // -moz-center and -moz-right are used for HTML's alignment
michael@0 321 // This is covering the <div align="right"><table>...</table></div> case.
michael@0 322 // In this case, we don't want to inherit the text alignment into the table.
michael@0 323 const nsStyleText* text = StyleText();
michael@0 324
michael@0 325 if (text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER ||
michael@0 326 text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)
michael@0 327 {
michael@0 328 nsStyleText* uniqueText = (nsStyleText*)GetUniqueStyleData(eStyleStruct_Text);
michael@0 329 uniqueText->mTextAlign = NS_STYLE_TEXT_ALIGN_DEFAULT;
michael@0 330 }
michael@0 331 }
michael@0 332
michael@0 333 // CSS2.1 section 9.2.4 specifies fixups for the 'display' property of
michael@0 334 // the root element. We can't implement them in nsRuleNode because we
michael@0 335 // don't want to store all display structs that aren't 'block',
michael@0 336 // 'inline', or 'table' in the style context tree on the off chance
michael@0 337 // that the root element has its style reresolved later. So do them
michael@0 338 // here if needed, by changing the style data, so that other code
michael@0 339 // doesn't get confused by looking at the style data.
michael@0 340 if (!mParent) {
michael@0 341 uint8_t displayVal = disp->mDisplay;
michael@0 342 nsRuleNode::EnsureBlockDisplay(displayVal, true);
michael@0 343 if (displayVal != disp->mDisplay) {
michael@0 344 nsStyleDisplay *mutable_display =
michael@0 345 static_cast<nsStyleDisplay*>(GetUniqueStyleData(eStyleStruct_Display));
michael@0 346
michael@0 347 // If we're in this code, then mOriginalDisplay doesn't matter
michael@0 348 // for purposes of the cascade (because this nsStyleDisplay
michael@0 349 // isn't living in the ruletree anyway), and for determining
michael@0 350 // hypothetical boxes it's better to have mOriginalDisplay
michael@0 351 // matching mDisplay here.
michael@0 352 mutable_display->mOriginalDisplay = mutable_display->mDisplay =
michael@0 353 displayVal;
michael@0 354 }
michael@0 355 }
michael@0 356
michael@0 357 // Adjust the "display" values of flex and grid items (but not for raw text,
michael@0 358 // placeholders, or table-parts). CSS3 Flexbox section 4 says:
michael@0 359 // # The computed 'display' of a flex item is determined
michael@0 360 // # by applying the table in CSS 2.1 Chapter 9.7.
michael@0 361 // ...which converts inline-level elements to their block-level equivalents.
michael@0 362 if (!aSkipFlexItemStyleFixup && mParent) {
michael@0 363 const nsStyleDisplay* parentDisp = mParent->StyleDisplay();
michael@0 364 if ((parentDisp->mDisplay == NS_STYLE_DISPLAY_FLEX ||
michael@0 365 parentDisp->mDisplay == NS_STYLE_DISPLAY_INLINE_FLEX ||
michael@0 366 parentDisp->mDisplay == NS_STYLE_DISPLAY_GRID ||
michael@0 367 parentDisp->mDisplay == NS_STYLE_DISPLAY_INLINE_GRID) &&
michael@0 368 GetPseudo() != nsCSSAnonBoxes::mozNonElement) {
michael@0 369 uint8_t displayVal = disp->mDisplay;
michael@0 370 // Skip table parts.
michael@0 371 // NOTE: This list needs to be kept in sync with
michael@0 372 // nsCSSFrameConstructor.cpp's "sDisplayData" array -- specifically,
michael@0 373 // this should be the list of display-values that have
michael@0 374 // FCDATA_DESIRED_PARENT_TYPE_TO_BITS specified in that array.
michael@0 375 if (NS_STYLE_DISPLAY_TABLE_CAPTION != displayVal &&
michael@0 376 NS_STYLE_DISPLAY_TABLE_ROW_GROUP != displayVal &&
michael@0 377 NS_STYLE_DISPLAY_TABLE_HEADER_GROUP != displayVal &&
michael@0 378 NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP != displayVal &&
michael@0 379 NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP != displayVal &&
michael@0 380 NS_STYLE_DISPLAY_TABLE_COLUMN != displayVal &&
michael@0 381 NS_STYLE_DISPLAY_TABLE_ROW != displayVal &&
michael@0 382 NS_STYLE_DISPLAY_TABLE_CELL != displayVal) {
michael@0 383
michael@0 384 // NOTE: Technically, we shouldn't modify the 'display' value of
michael@0 385 // positioned elements, since they aren't flex items. However, we don't
michael@0 386 // need to worry about checking for that, because if we're positioned,
michael@0 387 // we'll have already been through a call to EnsureBlockDisplay() in
michael@0 388 // nsRuleNode, so this call here won't change anything. So we're OK.
michael@0 389 nsRuleNode::EnsureBlockDisplay(displayVal);
michael@0 390 if (displayVal != disp->mDisplay) {
michael@0 391 NS_ASSERTION(!disp->IsAbsolutelyPositionedStyle(),
michael@0 392 "We shouldn't be changing the display value of "
michael@0 393 "positioned content (and we should have already "
michael@0 394 "converted its display value to be block-level...)");
michael@0 395 nsStyleDisplay *mutable_display =
michael@0 396 static_cast<nsStyleDisplay*>(GetUniqueStyleData(eStyleStruct_Display));
michael@0 397 mutable_display->mDisplay = displayVal;
michael@0 398 }
michael@0 399 }
michael@0 400 }
michael@0 401 }
michael@0 402
michael@0 403 // Compute User Interface style, to trigger loads of cursors
michael@0 404 StyleUserInterface();
michael@0 405 }
michael@0 406
michael@0 407 nsChangeHint
michael@0 408 nsStyleContext::CalcStyleDifference(nsStyleContext* aOther,
michael@0 409 nsChangeHint aParentHintsNotHandledForDescendants)
michael@0 410 {
michael@0 411 PROFILER_LABEL("nsStyleContext", "CalcStyleDifference");
michael@0 412
michael@0 413 NS_ABORT_IF_FALSE(NS_IsHintSubset(aParentHintsNotHandledForDescendants,
michael@0 414 nsChangeHint_Hints_NotHandledForDescendants),
michael@0 415 "caller is passing inherited hints, but shouldn't be");
michael@0 416
michael@0 417 nsChangeHint hint = NS_STYLE_HINT_NONE;
michael@0 418 NS_ENSURE_TRUE(aOther, hint);
michael@0 419 // We must always ensure that we populate the structs on the new style
michael@0 420 // context that are filled in on the old context, so that if we get
michael@0 421 // two style changes in succession, the second of which causes a real
michael@0 422 // style change, the PeekStyleData doesn't return null (implying that
michael@0 423 // nobody ever looked at that struct's data). In other words, we
michael@0 424 // can't skip later structs if we get a big change up front, because
michael@0 425 // we could later get a small change in one of those structs that we
michael@0 426 // don't want to miss.
michael@0 427
michael@0 428 // If our rule nodes are the same, then any differences in style data
michael@0 429 // are already accounted for by differences on ancestors. We know
michael@0 430 // this because CalcStyleDifference is always called on two style
michael@0 431 // contexts that point to the same element, so we know that our
michael@0 432 // position in the style context tree is the same and our position in
michael@0 433 // the rule node tree is also the same.
michael@0 434 // However, if there were noninherited style change hints on the
michael@0 435 // parent, we might produce these same noninherited hints on this
michael@0 436 // style context's frame due to 'inherit' values, so we do need to
michael@0 437 // compare.
michael@0 438 // (Things like 'em' units are handled by the change hint produced
michael@0 439 // by font-size changing, so we don't need to worry about them like
michael@0 440 // we worry about 'inherit' values.)
michael@0 441 bool compare = mRuleNode != aOther->mRuleNode;
michael@0 442
michael@0 443 // If we had any change in variable values, then we'll need to examine
michael@0 444 // all of the other style structs too, even if the new style context has
michael@0 445 // the same rule node as the old one.
michael@0 446 const nsStyleVariables* thisVariables = PeekStyleVariables();
michael@0 447 if (thisVariables) {
michael@0 448 const nsStyleVariables* otherVariables = aOther->StyleVariables();
michael@0 449 if (thisVariables->mVariables != otherVariables->mVariables) {
michael@0 450 compare = true;
michael@0 451 }
michael@0 452 }
michael@0 453
michael@0 454 DebugOnly<int> styleStructCount = 1; // count Variables already
michael@0 455
michael@0 456 #define DO_STRUCT_DIFFERENCE(struct_) \
michael@0 457 PR_BEGIN_MACRO \
michael@0 458 const nsStyle##struct_* this##struct_ = PeekStyle##struct_(); \
michael@0 459 if (this##struct_) { \
michael@0 460 const nsStyle##struct_* other##struct_ = aOther->Style##struct_(); \
michael@0 461 nsChangeHint maxDifference = nsStyle##struct_::MaxDifference(); \
michael@0 462 nsChangeHint maxDifferenceNeverInherited = \
michael@0 463 nsStyle##struct_::MaxDifferenceNeverInherited(); \
michael@0 464 if ((compare || \
michael@0 465 (NS_SubtractHint(maxDifference, maxDifferenceNeverInherited) & \
michael@0 466 aParentHintsNotHandledForDescendants)) && \
michael@0 467 !NS_IsHintSubset(maxDifference, hint) && \
michael@0 468 this##struct_ != other##struct_) { \
michael@0 469 NS_ASSERTION(NS_IsHintSubset( \
michael@0 470 this##struct_->CalcDifference(*other##struct_), \
michael@0 471 nsStyle##struct_::MaxDifference()), \
michael@0 472 "CalcDifference() returned bigger hint than MaxDifference()"); \
michael@0 473 NS_UpdateHint(hint, this##struct_->CalcDifference(*other##struct_)); \
michael@0 474 } \
michael@0 475 } \
michael@0 476 styleStructCount++; \
michael@0 477 PR_END_MACRO
michael@0 478
michael@0 479 // In general, we want to examine structs starting with those that can
michael@0 480 // cause the largest style change, down to those that can cause the
michael@0 481 // smallest. This lets us skip later ones if we already have a hint
michael@0 482 // that subsumes their MaxDifference. (As the hints get
michael@0 483 // finer-grained, this optimization is becoming less useful, though.)
michael@0 484 DO_STRUCT_DIFFERENCE(Display);
michael@0 485 DO_STRUCT_DIFFERENCE(XUL);
michael@0 486 DO_STRUCT_DIFFERENCE(Column);
michael@0 487 DO_STRUCT_DIFFERENCE(Content);
michael@0 488 DO_STRUCT_DIFFERENCE(UserInterface);
michael@0 489 DO_STRUCT_DIFFERENCE(Visibility);
michael@0 490 DO_STRUCT_DIFFERENCE(Outline);
michael@0 491 DO_STRUCT_DIFFERENCE(TableBorder);
michael@0 492 DO_STRUCT_DIFFERENCE(Table);
michael@0 493 DO_STRUCT_DIFFERENCE(UIReset);
michael@0 494 DO_STRUCT_DIFFERENCE(Text);
michael@0 495 DO_STRUCT_DIFFERENCE(List);
michael@0 496 DO_STRUCT_DIFFERENCE(Quotes);
michael@0 497 DO_STRUCT_DIFFERENCE(SVGReset);
michael@0 498 DO_STRUCT_DIFFERENCE(SVG);
michael@0 499 DO_STRUCT_DIFFERENCE(Position);
michael@0 500 DO_STRUCT_DIFFERENCE(Font);
michael@0 501 DO_STRUCT_DIFFERENCE(Margin);
michael@0 502 DO_STRUCT_DIFFERENCE(Padding);
michael@0 503 DO_STRUCT_DIFFERENCE(Border);
michael@0 504 DO_STRUCT_DIFFERENCE(TextReset);
michael@0 505 DO_STRUCT_DIFFERENCE(Background);
michael@0 506 DO_STRUCT_DIFFERENCE(Color);
michael@0 507
michael@0 508 #undef DO_STRUCT_DIFFERENCE
michael@0 509
michael@0 510 MOZ_ASSERT(styleStructCount == nsStyleStructID_Length,
michael@0 511 "missing a call to DO_STRUCT_DIFFERENCE");
michael@0 512
michael@0 513 // Note that we do not check whether this->RelevantLinkVisited() !=
michael@0 514 // aOther->RelevantLinkVisited(); we don't need to since
michael@0 515 // nsCSSFrameConstructor::DoContentStateChanged always adds
michael@0 516 // nsChangeHint_RepaintFrame for NS_EVENT_STATE_VISITED changes (and
michael@0 517 // needs to, since HasStateDependentStyle probably doesn't work right
michael@0 518 // for NS_EVENT_STATE_VISITED). Hopefully this doesn't actually
michael@0 519 // expose whether links are visited to performance tests since all
michael@0 520 // link coloring happens asynchronously at a time when it's hard for
michael@0 521 // the page to measure.
michael@0 522 // However, we do need to compute the larger of the changes that can
michael@0 523 // happen depending on whether the link is visited or unvisited, since
michael@0 524 // doing only the one that's currently appropriate would expose which
michael@0 525 // links are in history to easy performance measurement. Therefore,
michael@0 526 // here, we add nsChangeHint_RepaintFrame hints (the maximum for
michael@0 527 // things that can depend on :visited) for the properties on which we
michael@0 528 // call GetVisitedDependentColor.
michael@0 529 nsStyleContext *thisVis = GetStyleIfVisited(),
michael@0 530 *otherVis = aOther->GetStyleIfVisited();
michael@0 531 if (!thisVis != !otherVis) {
michael@0 532 // One style context has a style-if-visited and the other doesn't.
michael@0 533 // Presume a difference.
michael@0 534 NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
michael@0 535 } else if (thisVis && !NS_IsHintSubset(nsChangeHint_RepaintFrame, hint)) {
michael@0 536 // Both style contexts have a style-if-visited.
michael@0 537 bool change = false;
michael@0 538
michael@0 539 // NB: Calling Peek on |this|, not |thisVis|, since callers may look
michael@0 540 // at a struct on |this| without looking at the same struct on
michael@0 541 // |thisVis| (including this function if we skip one of these checks
michael@0 542 // due to change being true already or due to the old style context
michael@0 543 // not having a style-if-visited), but not the other way around.
michael@0 544 if (PeekStyleColor()) {
michael@0 545 if (thisVis->StyleColor()->mColor !=
michael@0 546 otherVis->StyleColor()->mColor) {
michael@0 547 change = true;
michael@0 548 }
michael@0 549 }
michael@0 550
michael@0 551 // NB: Calling Peek on |this|, not |thisVis| (see above).
michael@0 552 if (!change && PeekStyleBackground()) {
michael@0 553 if (thisVis->StyleBackground()->mBackgroundColor !=
michael@0 554 otherVis->StyleBackground()->mBackgroundColor) {
michael@0 555 change = true;
michael@0 556 }
michael@0 557 }
michael@0 558
michael@0 559 // NB: Calling Peek on |this|, not |thisVis| (see above).
michael@0 560 if (!change && PeekStyleBorder()) {
michael@0 561 const nsStyleBorder *thisVisBorder = thisVis->StyleBorder();
michael@0 562 const nsStyleBorder *otherVisBorder = otherVis->StyleBorder();
michael@0 563 NS_FOR_CSS_SIDES(side) {
michael@0 564 bool thisFG, otherFG;
michael@0 565 nscolor thisColor, otherColor;
michael@0 566 thisVisBorder->GetBorderColor(side, thisColor, thisFG);
michael@0 567 otherVisBorder->GetBorderColor(side, otherColor, otherFG);
michael@0 568 if (thisFG != otherFG || (!thisFG && thisColor != otherColor)) {
michael@0 569 change = true;
michael@0 570 break;
michael@0 571 }
michael@0 572 }
michael@0 573 }
michael@0 574
michael@0 575 // NB: Calling Peek on |this|, not |thisVis| (see above).
michael@0 576 if (!change && PeekStyleOutline()) {
michael@0 577 const nsStyleOutline *thisVisOutline = thisVis->StyleOutline();
michael@0 578 const nsStyleOutline *otherVisOutline = otherVis->StyleOutline();
michael@0 579 bool haveColor;
michael@0 580 nscolor thisColor, otherColor;
michael@0 581 if (thisVisOutline->GetOutlineInitialColor() !=
michael@0 582 otherVisOutline->GetOutlineInitialColor() ||
michael@0 583 (haveColor = thisVisOutline->GetOutlineColor(thisColor)) !=
michael@0 584 otherVisOutline->GetOutlineColor(otherColor) ||
michael@0 585 (haveColor && thisColor != otherColor)) {
michael@0 586 change = true;
michael@0 587 }
michael@0 588 }
michael@0 589
michael@0 590 // NB: Calling Peek on |this|, not |thisVis| (see above).
michael@0 591 if (!change && PeekStyleColumn()) {
michael@0 592 const nsStyleColumn *thisVisColumn = thisVis->StyleColumn();
michael@0 593 const nsStyleColumn *otherVisColumn = otherVis->StyleColumn();
michael@0 594 if (thisVisColumn->mColumnRuleColor != otherVisColumn->mColumnRuleColor ||
michael@0 595 thisVisColumn->mColumnRuleColorIsForeground !=
michael@0 596 otherVisColumn->mColumnRuleColorIsForeground) {
michael@0 597 change = true;
michael@0 598 }
michael@0 599 }
michael@0 600
michael@0 601 // NB: Calling Peek on |this|, not |thisVis| (see above).
michael@0 602 if (!change && PeekStyleTextReset()) {
michael@0 603 const nsStyleTextReset *thisVisTextReset = thisVis->StyleTextReset();
michael@0 604 const nsStyleTextReset *otherVisTextReset = otherVis->StyleTextReset();
michael@0 605 nscolor thisVisDecColor, otherVisDecColor;
michael@0 606 bool thisVisDecColorIsFG, otherVisDecColorIsFG;
michael@0 607 thisVisTextReset->GetDecorationColor(thisVisDecColor,
michael@0 608 thisVisDecColorIsFG);
michael@0 609 otherVisTextReset->GetDecorationColor(otherVisDecColor,
michael@0 610 otherVisDecColorIsFG);
michael@0 611 if (thisVisDecColorIsFG != otherVisDecColorIsFG ||
michael@0 612 (!thisVisDecColorIsFG && thisVisDecColor != otherVisDecColor)) {
michael@0 613 change = true;
michael@0 614 }
michael@0 615 }
michael@0 616
michael@0 617 // NB: Calling Peek on |this|, not |thisVis| (see above).
michael@0 618 if (!change && PeekStyleSVG()) {
michael@0 619 const nsStyleSVG *thisVisSVG = thisVis->StyleSVG();
michael@0 620 const nsStyleSVG *otherVisSVG = otherVis->StyleSVG();
michael@0 621 if (thisVisSVG->mFill != otherVisSVG->mFill ||
michael@0 622 thisVisSVG->mStroke != otherVisSVG->mStroke) {
michael@0 623 change = true;
michael@0 624 }
michael@0 625 }
michael@0 626
michael@0 627 if (change) {
michael@0 628 NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
michael@0 629 }
michael@0 630 }
michael@0 631
michael@0 632 return hint;
michael@0 633 }
michael@0 634
michael@0 635 void
michael@0 636 nsStyleContext::Mark()
michael@0 637 {
michael@0 638 // Mark our rule node.
michael@0 639 mRuleNode->Mark();
michael@0 640
michael@0 641 // Mark our children (i.e., tell them to mark their rule nodes, etc.).
michael@0 642 if (mChild) {
michael@0 643 nsStyleContext* child = mChild;
michael@0 644 do {
michael@0 645 child->Mark();
michael@0 646 child = child->mNextSibling;
michael@0 647 } while (mChild != child);
michael@0 648 }
michael@0 649
michael@0 650 if (mEmptyChild) {
michael@0 651 nsStyleContext* child = mEmptyChild;
michael@0 652 do {
michael@0 653 child->Mark();
michael@0 654 child = child->mNextSibling;
michael@0 655 } while (mEmptyChild != child);
michael@0 656 }
michael@0 657 }
michael@0 658
michael@0 659 #ifdef DEBUG
michael@0 660 void nsStyleContext::List(FILE* out, int32_t aIndent)
michael@0 661 {
michael@0 662 // Indent
michael@0 663 int32_t ix;
michael@0 664 for (ix = aIndent; --ix >= 0; ) fputs(" ", out);
michael@0 665 fprintf(out, "%p(%d) parent=%p ",
michael@0 666 (void*)this, mRefCnt, (void *)mParent);
michael@0 667 if (mPseudoTag) {
michael@0 668 nsAutoString buffer;
michael@0 669 mPseudoTag->ToString(buffer);
michael@0 670 fputs(NS_LossyConvertUTF16toASCII(buffer).get(), out);
michael@0 671 fputs(" ", out);
michael@0 672 }
michael@0 673
michael@0 674 if (mRuleNode) {
michael@0 675 fputs("{\n", out);
michael@0 676 nsRuleNode* ruleNode = mRuleNode;
michael@0 677 while (ruleNode) {
michael@0 678 nsIStyleRule *styleRule = ruleNode->GetRule();
michael@0 679 if (styleRule) {
michael@0 680 styleRule->List(out, aIndent + 1);
michael@0 681 }
michael@0 682 ruleNode = ruleNode->GetParent();
michael@0 683 }
michael@0 684 for (ix = aIndent; --ix >= 0; ) fputs(" ", out);
michael@0 685 fputs("}\n", out);
michael@0 686 }
michael@0 687 else {
michael@0 688 fputs("{}\n", out);
michael@0 689 }
michael@0 690
michael@0 691 if (nullptr != mChild) {
michael@0 692 nsStyleContext* child = mChild;
michael@0 693 do {
michael@0 694 child->List(out, aIndent + 1);
michael@0 695 child = child->mNextSibling;
michael@0 696 } while (mChild != child);
michael@0 697 }
michael@0 698 if (nullptr != mEmptyChild) {
michael@0 699 nsStyleContext* child = mEmptyChild;
michael@0 700 do {
michael@0 701 child->List(out, aIndent + 1);
michael@0 702 child = child->mNextSibling;
michael@0 703 } while (mEmptyChild != child);
michael@0 704 }
michael@0 705 }
michael@0 706 #endif
michael@0 707
michael@0 708 // Overloaded new operator. Initializes the memory to 0 and relies on an arena
michael@0 709 // (which comes from the presShell) to perform the allocation.
michael@0 710 void*
michael@0 711 nsStyleContext::operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW
michael@0 712 {
michael@0 713 // Check the recycle list first.
michael@0 714 return aPresContext->PresShell()->AllocateByObjectID(nsPresArena::nsStyleContext_id, sz);
michael@0 715 }
michael@0 716
michael@0 717 // Overridden to prevent the global delete from being called, since the memory
michael@0 718 // came out of an nsIArena instead of the global delete operator's heap.
michael@0 719 void
michael@0 720 nsStyleContext::Destroy()
michael@0 721 {
michael@0 722 // Get the pres context from our rule node.
michael@0 723 nsRefPtr<nsPresContext> presContext = mRuleNode->PresContext();
michael@0 724
michael@0 725 // Call our destructor.
michael@0 726 this->~nsStyleContext();
michael@0 727
michael@0 728 // Don't let the memory be freed, since it will be recycled
michael@0 729 // instead. Don't call the global operator delete.
michael@0 730 presContext->PresShell()->FreeByObjectID(nsPresArena::nsStyleContext_id, this);
michael@0 731 }
michael@0 732
michael@0 733 already_AddRefed<nsStyleContext>
michael@0 734 NS_NewStyleContext(nsStyleContext* aParentContext,
michael@0 735 nsIAtom* aPseudoTag,
michael@0 736 nsCSSPseudoElements::Type aPseudoType,
michael@0 737 nsRuleNode* aRuleNode,
michael@0 738 bool aSkipFlexItemStyleFixup)
michael@0 739 {
michael@0 740 nsRefPtr<nsStyleContext> context =
michael@0 741 new (aRuleNode->PresContext())
michael@0 742 nsStyleContext(aParentContext, aPseudoTag, aPseudoType, aRuleNode,
michael@0 743 aSkipFlexItemStyleFixup);
michael@0 744 return context.forget();
michael@0 745 }
michael@0 746
michael@0 747 static inline void
michael@0 748 ExtractAnimationValue(nsCSSProperty aProperty,
michael@0 749 nsStyleContext* aStyleContext,
michael@0 750 nsStyleAnimation::Value& aResult)
michael@0 751 {
michael@0 752 DebugOnly<bool> success =
michael@0 753 nsStyleAnimation::ExtractComputedValue(aProperty, aStyleContext, aResult);
michael@0 754 NS_ABORT_IF_FALSE(success,
michael@0 755 "aProperty must be extractable by nsStyleAnimation");
michael@0 756 }
michael@0 757
michael@0 758 static nscolor
michael@0 759 ExtractColor(nsCSSProperty aProperty,
michael@0 760 nsStyleContext *aStyleContext)
michael@0 761 {
michael@0 762 nsStyleAnimation::Value val;
michael@0 763 ExtractAnimationValue(aProperty, aStyleContext, val);
michael@0 764 return val.GetColorValue();
michael@0 765 }
michael@0 766
michael@0 767 static nscolor
michael@0 768 ExtractColorLenient(nsCSSProperty aProperty,
michael@0 769 nsStyleContext *aStyleContext)
michael@0 770 {
michael@0 771 nsStyleAnimation::Value val;
michael@0 772 ExtractAnimationValue(aProperty, aStyleContext, val);
michael@0 773 if (val.GetUnit() == nsStyleAnimation::eUnit_Color) {
michael@0 774 return val.GetColorValue();
michael@0 775 }
michael@0 776 return NS_RGBA(0, 0, 0, 0);
michael@0 777 }
michael@0 778
michael@0 779 struct ColorIndexSet {
michael@0 780 uint8_t colorIndex, alphaIndex;
michael@0 781 };
michael@0 782
michael@0 783 static const ColorIndexSet gVisitedIndices[2] = { { 0, 0 }, { 1, 0 } };
michael@0 784
michael@0 785 nscolor
michael@0 786 nsStyleContext::GetVisitedDependentColor(nsCSSProperty aProperty)
michael@0 787 {
michael@0 788 NS_ASSERTION(aProperty == eCSSProperty_color ||
michael@0 789 aProperty == eCSSProperty_background_color ||
michael@0 790 aProperty == eCSSProperty_border_top_color ||
michael@0 791 aProperty == eCSSProperty_border_right_color_value ||
michael@0 792 aProperty == eCSSProperty_border_bottom_color ||
michael@0 793 aProperty == eCSSProperty_border_left_color_value ||
michael@0 794 aProperty == eCSSProperty_outline_color ||
michael@0 795 aProperty == eCSSProperty__moz_column_rule_color ||
michael@0 796 aProperty == eCSSProperty_text_decoration_color ||
michael@0 797 aProperty == eCSSProperty_fill ||
michael@0 798 aProperty == eCSSProperty_stroke,
michael@0 799 "we need to add to nsStyleContext::CalcStyleDifference");
michael@0 800
michael@0 801 bool isPaintProperty = aProperty == eCSSProperty_fill ||
michael@0 802 aProperty == eCSSProperty_stroke;
michael@0 803
michael@0 804 nscolor colors[2];
michael@0 805 colors[0] = isPaintProperty ? ExtractColorLenient(aProperty, this)
michael@0 806 : ExtractColor(aProperty, this);
michael@0 807
michael@0 808 nsStyleContext *visitedStyle = this->GetStyleIfVisited();
michael@0 809 if (!visitedStyle) {
michael@0 810 return colors[0];
michael@0 811 }
michael@0 812
michael@0 813 colors[1] = isPaintProperty ? ExtractColorLenient(aProperty, visitedStyle)
michael@0 814 : ExtractColor(aProperty, visitedStyle);
michael@0 815
michael@0 816 return nsStyleContext::CombineVisitedColors(colors,
michael@0 817 this->RelevantLinkVisited());
michael@0 818 }
michael@0 819
michael@0 820 /* static */ nscolor
michael@0 821 nsStyleContext::CombineVisitedColors(nscolor *aColors, bool aLinkIsVisited)
michael@0 822 {
michael@0 823 if (NS_GET_A(aColors[1]) == 0) {
michael@0 824 // If the style-if-visited is transparent, then just use the
michael@0 825 // unvisited style rather than using the (meaningless) color
michael@0 826 // components of the visited style along with a potentially
michael@0 827 // non-transparent alpha value.
michael@0 828 aLinkIsVisited = false;
michael@0 829 }
michael@0 830
michael@0 831 // NOTE: We want this code to have as little timing dependence as
michael@0 832 // possible on whether this->RelevantLinkVisited() is true.
michael@0 833 const ColorIndexSet &set =
michael@0 834 gVisitedIndices[aLinkIsVisited ? 1 : 0];
michael@0 835
michael@0 836 nscolor colorColor = aColors[set.colorIndex];
michael@0 837 nscolor alphaColor = aColors[set.alphaIndex];
michael@0 838 return NS_RGBA(NS_GET_R(colorColor), NS_GET_G(colorColor),
michael@0 839 NS_GET_B(colorColor), NS_GET_A(alphaColor));
michael@0 840 }
michael@0 841
michael@0 842 void*
michael@0 843 nsStyleContext::Alloc(size_t aSize)
michael@0 844 {
michael@0 845 nsIPresShell *shell = PresContext()->PresShell();
michael@0 846
michael@0 847 aSize += offsetof(AllocationHeader, mStorageStart);
michael@0 848 AllocationHeader *alloc =
michael@0 849 static_cast<AllocationHeader*>(shell->AllocateMisc(aSize));
michael@0 850
michael@0 851 alloc->mSize = aSize; // NOTE: inflated by header
michael@0 852
michael@0 853 alloc->mNext = mAllocations;
michael@0 854 mAllocations = alloc;
michael@0 855
michael@0 856 return static_cast<void*>(&alloc->mStorageStart);
michael@0 857 }
michael@0 858
michael@0 859 void
michael@0 860 nsStyleContext::FreeAllocations(nsPresContext *aPresContext)
michael@0 861 {
michael@0 862 nsIPresShell *shell = aPresContext->PresShell();
michael@0 863
michael@0 864 for (AllocationHeader *alloc = mAllocations, *next; alloc; alloc = next) {
michael@0 865 next = alloc->mNext;
michael@0 866 shell->FreeMisc(alloc->mSize, alloc);
michael@0 867 }
michael@0 868 }
michael@0 869
michael@0 870 #ifdef DEBUG
michael@0 871 /* static */ void
michael@0 872 nsStyleContext::AssertStyleStructMaxDifferenceValid()
michael@0 873 {
michael@0 874 #define STYLE_STRUCT(name, checkdata_cb) \
michael@0 875 MOZ_ASSERT(NS_IsHintSubset(nsStyle##name::MaxDifferenceNeverInherited(), \
michael@0 876 nsStyle##name::MaxDifference()));
michael@0 877 #include "nsStyleStructList.h"
michael@0 878 #undef STYLE_STRUCT
michael@0 879 }
michael@0 880 #endif

mercurial