Wed, 31 Dec 2014 07:16:47 +0100
Revert simplistic fix pending revisit of Mozilla integration attempt.
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 |