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