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"
13 #include "nsPresContext.h"
14 #include "nsIStyleRule.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"
24 #include "nsIDocument.h"
25 #include "nsPrintfCString.h"
28 // #define NOISY_DEBUG
31 using namespace mozilla
;
33 //----------------------------------------------------------------------
36 nsStyleContext::nsStyleContext(nsStyleContext
* aParent
,
38 nsCSSPseudoElements::Type aPseudoType
,
39 nsRuleNode
* aRuleNode
,
40 bool aSkipParentDisplayBasedStyleFixup
)
44 mPseudoTag(aPseudoTag
),
46 mCachedResetData(nullptr),
47 mBits(((uint64_t)aPseudoType
) << NS_STYLE_CONTEXT_TYPE_SHIFT
),
53 // This check has to be done "backward", because if it were written the
54 // more natural way it wouldn't fail even when it needed to.
55 static_assert((UINT64_MAX
>> NS_STYLE_CONTEXT_TYPE_SHIFT
) >=
56 nsCSSPseudoElements::ePseudo_MAX
,
57 "pseudo element bits no longer fit in a uint64_t");
58 MOZ_ASSERT(aRuleNode
);
64 mParent
->AddChild(this);
66 nsRuleNode
*r1
= mParent
->RuleNode(), *r2
= aRuleNode
;
67 while (r1
->GetParent())
69 while (r2
->GetParent())
71 NS_ASSERTION(r1
== r2
, "must be in the same rule tree as parent");
76 mRuleNode
->SetUsedDirectly(); // before ApplyStyleFixups()!
78 ApplyStyleFixups(aSkipParentDisplayBasedStyleFixup
);
80 #define eStyleStruct_LastItem (nsStyleStructID_Length - 1)
81 NS_ASSERTION(NS_STYLE_INHERIT_MASK
& NS_STYLE_INHERIT_BIT(LastItem
),
82 "NS_STYLE_INHERIT_MASK must be bigger, and other bits shifted");
83 #undef eStyleStruct_LastItem
86 nsStyleContext::~nsStyleContext()
88 NS_ASSERTION((nullptr == mChild
) && (nullptr == mEmptyChild
), "destructing context with children");
90 nsPresContext
*presContext
= mRuleNode
->PresContext();
91 nsStyleSet
* styleSet
= presContext
->PresShell()->StyleSet();
93 NS_ASSERTION(styleSet
->GetRuleTree() == mRuleNode
->RuleTree() ||
94 styleSet
->IsInRuleTreeReconstruct(),
95 "destroying style context from old rule tree too late");
99 // Assert that the style structs we are about to destroy are not referenced
100 // anywhere else in the style context tree. These checks are expensive,
101 // which is why they are not enabled even #ifdef DEBUG.
102 nsStyleContext
* root
= this;
103 while (root
->mParent
) {
104 root
= root
->mParent
;
106 root
->AssertStructsNotUsedElsewhere(this,
107 std::numeric_limits
<int32_t>::max());
109 // In DEBUG builds we perform a more limited check just of the children
110 // of this style context.
111 AssertStructsNotUsedElsewhere(this, 2);
115 mRuleNode
->Release();
117 styleSet
->NotifyStyleContextDestroyed(presContext
, this);
120 mParent
->RemoveChild(this);
124 // Free up our data structs.
125 mCachedInheritedData
.DestroyStructs(mBits
, presContext
);
126 if (mCachedResetData
) {
127 mCachedResetData
->Destroy(mBits
, presContext
);
133 nsStyleContext::AssertStructsNotUsedElsewhere(
134 nsStyleContext
* aDestroyingContext
,
135 int32_t aLevels
) const
143 if (mBits
& NS_STYLE_IS_GOING_AWAY
) {
147 if (this != aDestroyingContext
) {
148 nsInheritedStyleData
& destroyingInheritedData
=
149 aDestroyingContext
->mCachedInheritedData
;
150 #define STYLE_STRUCT_INHERITED(name_, checkdata_cb) \
151 data = destroyingInheritedData.mStyleStructs[eStyleStruct_##name_]; \
153 !(aDestroyingContext->mBits & NS_STYLE_INHERIT_BIT(name_)) && \
154 (mCachedInheritedData.mStyleStructs[eStyleStruct_##name_] == data)) { \
155 printf_stderr("style struct %p found on style context %p\n", data, this);\
157 PresContext()->Document()->GetURL(url); \
158 printf_stderr(" in %s\n", NS_ConvertUTF16toUTF8(url).get()); \
159 MOZ_ASSERT(false, "destroying " #name_ " style struct still present " \
160 "in style context tree"); \
162 #define STYLE_STRUCT_RESET(name_, checkdata_cb)
164 #include "nsStyleStructList.h"
166 #undef STYLE_STRUCT_INHERITED
167 #undef STYLE_STRUCT_RESET
169 if (mCachedResetData
) {
170 nsResetStyleData
* destroyingResetData
=
171 aDestroyingContext
->mCachedResetData
;
172 if (destroyingResetData
) {
173 #define STYLE_STRUCT_INHERITED(name_, checkdata_cb_)
174 #define STYLE_STRUCT_RESET(name_, checkdata_cb) \
175 data = destroyingResetData->mStyleStructs[eStyleStruct_##name_]; \
177 !(aDestroyingContext->mBits & NS_STYLE_INHERIT_BIT(name_)) && \
178 (mCachedResetData->mStyleStructs[eStyleStruct_##name_] == data)) { \
179 printf_stderr("style struct %p found on style context %p\n", data, \
182 PresContext()->Document()->GetURL(url); \
183 printf_stderr(" in %s\n", NS_ConvertUTF16toUTF8(url).get()); \
184 MOZ_ASSERT(false, "destroying " #name_ " style struct still present "\
185 "in style context tree"); \
188 #include "nsStyleStructList.h"
190 #undef STYLE_STRUCT_INHERITED
191 #undef STYLE_STRUCT_RESET
197 const nsStyleContext
* child
= mChild
;
199 child
->AssertStructsNotUsedElsewhere(aDestroyingContext
, aLevels
- 1);
200 child
= child
->mNextSibling
;
201 } while (child
!= mChild
);
205 const nsStyleContext
* child
= mEmptyChild
;
207 child
->AssertStructsNotUsedElsewhere(aDestroyingContext
, aLevels
- 1);
208 child
= child
->mNextSibling
;
209 } while (child
!= mEmptyChild
);
214 void nsStyleContext::AddChild(nsStyleContext
* aChild
)
216 NS_ASSERTION(aChild
->mPrevSibling
== aChild
&&
217 aChild
->mNextSibling
== aChild
,
218 "child already in a child list");
220 nsStyleContext
**listPtr
= aChild
->mRuleNode
->IsRoot() ? &mEmptyChild
: &mChild
;
221 // Explicitly dereference listPtr so that compiler doesn't have to know that mNextSibling
222 // etc. don't alias with what ever listPtr points at.
223 nsStyleContext
*list
= *listPtr
;
225 // Insert at the beginning of the list. See also FindChildWithRules.
227 // Link into existing elements, if there are any.
228 aChild
->mNextSibling
= list
;
229 aChild
->mPrevSibling
= list
->mPrevSibling
;
230 list
->mPrevSibling
->mNextSibling
= aChild
;
231 list
->mPrevSibling
= aChild
;
236 void nsStyleContext::RemoveChild(nsStyleContext
* aChild
)
238 NS_PRECONDITION(nullptr != aChild
&& this == aChild
->mParent
, "bad argument");
240 nsStyleContext
**list
= aChild
->mRuleNode
->IsRoot() ? &mEmptyChild
: &mChild
;
242 if (aChild
->mPrevSibling
!= aChild
) { // has siblings
243 if ((*list
) == aChild
) {
244 (*list
) = (*list
)->mNextSibling
;
248 NS_ASSERTION((*list
) == aChild
, "bad sibling pointers");
252 aChild
->mPrevSibling
->mNextSibling
= aChild
->mNextSibling
;
253 aChild
->mNextSibling
->mPrevSibling
= aChild
->mPrevSibling
;
254 aChild
->mNextSibling
= aChild
;
255 aChild
->mPrevSibling
= aChild
;
259 nsStyleContext::MoveTo(nsStyleContext
* aNewParent
)
261 MOZ_ASSERT(aNewParent
!= mParent
);
263 // Assertions checking for visited style are just to avoid some tricky
264 // cases we can't be bothered handling at the moment.
265 MOZ_ASSERT(!IsStyleIfVisited());
266 MOZ_ASSERT(!aNewParent
->IsStyleIfVisited());
268 nsStyleContext
* oldParent
= mParent
;
270 aNewParent
->AddRef();
272 mParent
->RemoveChild(this);
274 mParent
= aNewParent
;
275 mParent
->AddChild(this);
277 oldParent
->Release();
280 already_AddRefed
<nsStyleContext
>
281 nsStyleContext::FindChildWithRules(const nsIAtom
* aPseudoTag
,
282 nsRuleNode
* aRuleNode
,
283 nsRuleNode
* aRulesIfVisited
,
284 bool aRelevantLinkVisited
)
286 uint32_t threshold
= 10; // The # of siblings we're willing to examine
287 // before just giving this whole thing up.
289 nsRefPtr
<nsStyleContext
> result
;
290 nsStyleContext
*list
= aRuleNode
->IsRoot() ? mEmptyChild
: mChild
;
293 nsStyleContext
*child
= list
;
295 if (child
->mRuleNode
== aRuleNode
&&
296 child
->mPseudoTag
== aPseudoTag
&&
297 !child
->IsStyleIfVisited() &&
298 child
->RelevantLinkVisited() == aRelevantLinkVisited
) {
300 if (aRulesIfVisited
) {
301 match
= child
->GetStyleIfVisited() &&
302 child
->GetStyleIfVisited()->mRuleNode
== aRulesIfVisited
;
304 match
= !child
->GetStyleIfVisited();
311 child
= child
->mNextSibling
;
315 } while (child
!= list
);
319 if (result
!= list
) {
320 // Move result to the front of the list.
324 result
->mBits
|= NS_STYLE_IS_SHARED
;
327 return result
.forget();
331 nsStyleContext::ListContainsStyleContextThatUsesGrandancestorStyle(const nsStyleContext
* aHead
)
334 const nsStyleContext
* child
= aHead
;
336 if (child
->UsesGrandancestorStyle()) {
339 child
= child
->mNextSibling
;
340 } while (child
!= aHead
);
347 nsStyleContext::HasChildThatUsesGrandancestorStyle() const
349 return ListContainsStyleContextThatUsesGrandancestorStyle(mEmptyChild
) ||
350 ListContainsStyleContextThatUsesGrandancestorStyle(mChild
);
353 const void* nsStyleContext::GetCachedStyleData(nsStyleStructID aSID
)
355 const void* cachedData
;
356 if (nsCachedStyleData::IsReset(aSID
)) {
357 if (mCachedResetData
) {
358 cachedData
= mCachedResetData
->mStyleStructs
[aSID
];
360 cachedData
= nullptr;
363 cachedData
= mCachedInheritedData
.mStyleStructs
[aSID
];
368 const void* nsStyleContext::StyleData(nsStyleStructID aSID
)
370 const void* cachedData
= GetCachedStyleData(aSID
);
372 return cachedData
; // We have computed data stored on this node in the context tree.
373 return mRuleNode
->GetStyleData(aSID
, this, true); // Our rule node will take care of it for us.
376 // This is an evil evil function, since it forces you to alloc your own separate copy of
377 // style data! Do not use this function unless you absolutely have to! You should avoid
378 // this at all costs! -dwh
380 nsStyleContext::GetUniqueStyleData(const nsStyleStructID
& aSID
)
382 // If we already own the struct and no kids could depend on it, then
383 // just return it. (We leak in this case if there are kids -- and this
384 // function really shouldn't be called for style contexts that could
385 // have kids depending on the data. ClearStyleData would be OK, but
386 // this test for no mChild or mEmptyChild doesn't catch that case.)
387 const void *current
= StyleData(aSID
);
388 if (!mChild
&& !mEmptyChild
&&
389 !(mBits
& nsCachedStyleData::GetBitForSID(aSID
)) &&
390 GetCachedStyleData(aSID
))
391 return const_cast<void*>(current
);
394 nsPresContext
*presContext
= PresContext();
397 #define UNIQUE_CASE(c_) \
398 case eStyleStruct_##c_: \
399 result = new (presContext) nsStyle##c_( \
400 * static_cast<const nsStyle##c_ *>(current)); \
404 UNIQUE_CASE(Background
)
406 UNIQUE_CASE(TextReset
)
411 NS_ERROR("Struct type not supported. Please find another way to do this if you can!");
415 SetStyle(aSID
, result
);
416 mBits
&= ~static_cast<uint64_t>(nsCachedStyleData::GetBitForSID(aSID
));
422 nsStyleContext::SetStyle(nsStyleStructID aSID
, void* aStruct
)
424 // This method should only be called from nsRuleNode! It is not a public
427 NS_ASSERTION(aSID
>= 0 && aSID
< nsStyleStructID_Length
, "out of bounds");
429 // NOTE: nsCachedStyleData::GetStyleData works roughly the same way.
430 // See the comments there (in nsRuleNode.h) for more details about
431 // what this is doing and why.
434 if (nsCachedStyleData::IsReset(aSID
)) {
435 if (!mCachedResetData
) {
436 mCachedResetData
= new (mRuleNode
->PresContext()) nsResetStyleData
;
438 dataSlot
= &mCachedResetData
->mStyleStructs
[aSID
];
440 dataSlot
= &mCachedInheritedData
.mStyleStructs
[aSID
];
442 NS_ASSERTION(!*dataSlot
|| (mBits
& nsCachedStyleData::GetBitForSID(aSID
)),
443 "Going to leak style data");
448 nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup
)
450 // See if we have any text decorations.
451 // First see if our parent has text decorations. If our parent does, then we inherit the bit.
452 if (mParent
&& mParent
->HasTextDecorationLines()) {
453 mBits
|= NS_STYLE_HAS_TEXT_DECORATION_LINES
;
455 // We might have defined a decoration.
456 const nsStyleTextReset
* text
= StyleTextReset();
457 uint8_t decorationLine
= text
->mTextDecorationLine
;
458 if (decorationLine
!= NS_STYLE_TEXT_DECORATION_LINE_NONE
&&
459 decorationLine
!= NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL
) {
460 mBits
|= NS_STYLE_HAS_TEXT_DECORATION_LINES
;
464 if ((mParent
&& mParent
->HasPseudoElementData()) || mPseudoTag
) {
465 mBits
|= NS_STYLE_HAS_PSEUDO_ELEMENT_DATA
;
469 const nsStyleDisplay
* disp
= StyleDisplay();
470 if (disp
->mDisplay
== NS_STYLE_DISPLAY_TABLE
) {
471 // -moz-center and -moz-right are used for HTML's alignment
472 // This is covering the <div align="right"><table>...</table></div> case.
473 // In this case, we don't want to inherit the text alignment into the table.
474 const nsStyleText
* text
= StyleText();
476 if (text
->mTextAlign
== NS_STYLE_TEXT_ALIGN_MOZ_CENTER
||
477 text
->mTextAlign
== NS_STYLE_TEXT_ALIGN_MOZ_RIGHT
)
479 nsStyleText
* uniqueText
= (nsStyleText
*)GetUniqueStyleData(eStyleStruct_Text
);
480 uniqueText
->mTextAlign
= NS_STYLE_TEXT_ALIGN_DEFAULT
;
484 // CSS2.1 section 9.2.4 specifies fixups for the 'display' property of
485 // the root element. We can't implement them in nsRuleNode because we
486 // don't want to store all display structs that aren't 'block',
487 // 'inline', or 'table' in the style context tree on the off chance
488 // that the root element has its style reresolved later. So do them
489 // here if needed, by changing the style data, so that other code
490 // doesn't get confused by looking at the style data.
492 uint8_t displayVal
= disp
->mDisplay
;
493 nsRuleNode::EnsureBlockDisplay(displayVal
, true);
494 if (displayVal
!= disp
->mDisplay
) {
495 nsStyleDisplay
*mutable_display
=
496 static_cast<nsStyleDisplay
*>(GetUniqueStyleData(eStyleStruct_Display
));
498 // If we're in this code, then mOriginalDisplay doesn't matter
499 // for purposes of the cascade (because this nsStyleDisplay
500 // isn't living in the ruletree anyway), and for determining
501 // hypothetical boxes it's better to have mOriginalDisplay
502 // matching mDisplay here.
503 mutable_display
->mOriginalDisplay
= mutable_display
->mDisplay
=
508 // Adjust the "display" values of flex and grid items (but not for raw text,
509 // placeholders, or table-parts). CSS3 Flexbox section 4 says:
510 // # The computed 'display' of a flex item is determined
511 // # by applying the table in CSS 2.1 Chapter 9.7.
512 // ...which converts inline-level elements to their block-level equivalents.
513 // Any block-level element directly contained by elements with ruby display
514 // values are converted to their inline-level equivalents.
515 if (!aSkipParentDisplayBasedStyleFixup
&& mParent
) {
516 // Skip display:contents ancestors to reach the potential container.
517 // (If there are only display:contents ancestors between this node and
518 // a flex/grid container ancestor, then this node is a flex/grid item, since
519 // its parent *in the frame tree* will be the flex/grid container. So we treat
520 // it like a flex/grid item here.)
521 nsStyleContext
* containerContext
= mParent
;
522 const nsStyleDisplay
* containerDisp
= containerContext
->StyleDisplay();
523 while (containerDisp
->mDisplay
== NS_STYLE_DISPLAY_CONTENTS
) {
524 if (!containerContext
->GetParent()) {
527 containerContext
= containerContext
->GetParent();
528 containerDisp
= containerContext
->StyleDisplay();
530 if (containerDisp
->IsFlexOrGridDisplayType() &&
531 GetPseudo() != nsCSSAnonBoxes::mozNonElement
) {
532 uint8_t displayVal
= disp
->mDisplay
;
534 // NOTE: This list needs to be kept in sync with
535 // nsCSSFrameConstructor::FindDisplayData() -- specifically,
536 // this should be the list of display-values that returns
537 // FCDATA_DESIRED_PARENT_TYPE_TO_BITS from that method.
538 if (NS_STYLE_DISPLAY_TABLE_CAPTION
!= displayVal
&&
539 NS_STYLE_DISPLAY_TABLE_ROW_GROUP
!= displayVal
&&
540 NS_STYLE_DISPLAY_TABLE_HEADER_GROUP
!= displayVal
&&
541 NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP
!= displayVal
&&
542 NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP
!= displayVal
&&
543 NS_STYLE_DISPLAY_TABLE_COLUMN
!= displayVal
&&
544 NS_STYLE_DISPLAY_TABLE_ROW
!= displayVal
&&
545 NS_STYLE_DISPLAY_TABLE_CELL
!= displayVal
) {
547 // NOTE: Technically, we shouldn't modify the 'display' value of
548 // positioned elements, since they aren't flex/grid items. However,
549 // we don't need to worry about checking for that, because if we're
550 // positioned, we'll have already been through a call to
551 // EnsureBlockDisplay() in nsRuleNode, so this call here won't change
552 // anything. So we're OK.
553 nsRuleNode::EnsureBlockDisplay(displayVal
);
554 if (displayVal
!= disp
->mDisplay
) {
555 NS_ASSERTION(!disp
->IsAbsolutelyPositionedStyle(),
556 "We shouldn't be changing the display value of "
557 "positioned content (and we should have already "
558 "converted its display value to be block-level...)");
559 nsStyleDisplay
*mutable_display
=
560 static_cast<nsStyleDisplay
*>(GetUniqueStyleData(eStyleStruct_Display
));
561 mutable_display
->mDisplay
= displayVal
;
566 // The display change should only occur for "in-flow" children
567 if (!disp
->IsOutOfFlowStyle() &&
568 ((containerDisp
->mDisplay
== NS_STYLE_DISPLAY_INLINE
&&
569 containerContext
->IsInlineDescendantOfRuby()) ||
570 containerDisp
->IsRubyDisplayType())) {
571 mBits
|= NS_STYLE_IS_INLINE_DESCENDANT_OF_RUBY
;
572 uint8_t displayVal
= disp
->mDisplay
;
573 nsRuleNode::EnsureInlineDisplay(displayVal
);
574 if (displayVal
!= disp
->mDisplay
) {
575 nsStyleDisplay
*mutable_display
=
576 static_cast<nsStyleDisplay
*>(GetUniqueStyleData(eStyleStruct_Display
));
577 mutable_display
->mDisplay
= displayVal
;
582 // Compute User Interface style, to trigger loads of cursors
583 StyleUserInterface();
587 nsStyleContext::CalcStyleDifference(nsStyleContext
* aOther
,
588 nsChangeHint aParentHintsNotHandledForDescendants
,
589 uint32_t* aEqualStructs
)
591 PROFILER_LABEL("nsStyleContext", "CalcStyleDifference",
592 js::ProfileEntry::Category::CSS
);
594 NS_ABORT_IF_FALSE(NS_IsHintSubset(aParentHintsNotHandledForDescendants
,
595 nsChangeHint_Hints_NotHandledForDescendants
),
596 "caller is passing inherited hints, but shouldn't be");
598 static_assert(nsStyleStructID_Length
<= 32,
599 "aEqualStructs is not big enough");
603 nsChangeHint hint
= NS_STYLE_HINT_NONE
;
604 NS_ENSURE_TRUE(aOther
, hint
);
605 // We must always ensure that we populate the structs on the new style
606 // context that are filled in on the old context, so that if we get
607 // two style changes in succession, the second of which causes a real
608 // style change, the PeekStyleData doesn't return null (implying that
609 // nobody ever looked at that struct's data). In other words, we
610 // can't skip later structs if we get a big change up front, because
611 // we could later get a small change in one of those structs that we
612 // don't want to miss.
614 // If our rule nodes are the same, then any differences in style data
615 // are already accounted for by differences on ancestors. We know
616 // this because CalcStyleDifference is always called on two style
617 // contexts that point to the same element, so we know that our
618 // position in the style context tree is the same and our position in
619 // the rule node tree is also the same.
620 // However, if there were noninherited style change hints on the
621 // parent, we might produce these same noninherited hints on this
622 // style context's frame due to 'inherit' values, so we do need to
624 // (Things like 'em' units are handled by the change hint produced
625 // by font-size changing, so we don't need to worry about them like
626 // we worry about 'inherit' values.)
627 bool compare
= mRuleNode
!= aOther
->mRuleNode
;
629 // If we had any change in variable values, then we'll need to examine
630 // all of the other style structs too, even if the new style context has
631 // the same rule node as the old one.
632 const nsStyleVariables
* thisVariables
= PeekStyleVariables();
634 const nsStyleVariables
* otherVariables
= aOther
->StyleVariables();
635 if (thisVariables
->mVariables
== otherVariables
->mVariables
) {
636 *aEqualStructs
|= nsCachedStyleData::GetBitForSID(eStyleStruct_Variables
);
641 *aEqualStructs
|= nsCachedStyleData::GetBitForSID(eStyleStruct_Variables
);
644 DebugOnly
<int> styleStructCount
= 1; // count Variables already
646 #define DO_STRUCT_DIFFERENCE(struct_) \
648 const nsStyle##struct_* this##struct_ = PeekStyle##struct_(); \
649 if (this##struct_) { \
650 const nsStyle##struct_* other##struct_ = aOther->Style##struct_(); \
651 nsChangeHint maxDifference = nsStyle##struct_::MaxDifference(); \
652 nsChangeHint maxDifferenceNeverInherited = \
653 nsStyle##struct_::MaxDifferenceNeverInherited(); \
654 if (this##struct_ == other##struct_) { \
655 /* The very same struct, so we know that there will be no */ \
657 *aEqualStructs |= NS_STYLE_INHERIT_BIT(struct_); \
658 } else if (compare || \
659 (NS_SubtractHint(maxDifference, \
660 maxDifferenceNeverInherited) & \
661 aParentHintsNotHandledForDescendants)) { \
662 nsChangeHint difference = \
663 this##struct_->CalcDifference(*other##struct_); \
664 NS_ASSERTION(NS_IsHintSubset(difference, maxDifference), \
665 "CalcDifference() returned bigger hint than " \
666 "MaxDifference()"); \
667 NS_UpdateHint(hint, difference); \
669 *aEqualStructs |= NS_STYLE_INHERIT_BIT(struct_); \
672 /* We still must call CalcDifference to see if there were any */ \
673 /* changes so that we can set *aEqualStructs appropriately. */ \
674 nsChangeHint difference = \
675 this##struct_->CalcDifference(*other##struct_); \
676 NS_ASSERTION(NS_IsHintSubset(difference, maxDifference), \
677 "CalcDifference() returned bigger hint than " \
678 "MaxDifference()"); \
680 *aEqualStructs |= NS_STYLE_INHERIT_BIT(struct_); \
684 *aEqualStructs |= NS_STYLE_INHERIT_BIT(struct_); \
686 styleStructCount++; \
689 // In general, we want to examine structs starting with those that can
690 // cause the largest style change, down to those that can cause the
691 // smallest. This lets us skip later ones if we already have a hint
692 // that subsumes their MaxDifference. (As the hints get
693 // finer-grained, this optimization is becoming less useful, though.)
694 DO_STRUCT_DIFFERENCE(Display
);
695 DO_STRUCT_DIFFERENCE(XUL
);
696 DO_STRUCT_DIFFERENCE(Column
);
697 DO_STRUCT_DIFFERENCE(Content
);
698 DO_STRUCT_DIFFERENCE(UserInterface
);
699 DO_STRUCT_DIFFERENCE(Visibility
);
700 DO_STRUCT_DIFFERENCE(Outline
);
701 DO_STRUCT_DIFFERENCE(TableBorder
);
702 DO_STRUCT_DIFFERENCE(Table
);
703 DO_STRUCT_DIFFERENCE(UIReset
);
704 DO_STRUCT_DIFFERENCE(Text
);
705 DO_STRUCT_DIFFERENCE(List
);
706 DO_STRUCT_DIFFERENCE(Quotes
);
707 DO_STRUCT_DIFFERENCE(SVGReset
);
708 DO_STRUCT_DIFFERENCE(SVG
);
709 DO_STRUCT_DIFFERENCE(Position
);
710 DO_STRUCT_DIFFERENCE(Font
);
711 DO_STRUCT_DIFFERENCE(Margin
);
712 DO_STRUCT_DIFFERENCE(Padding
);
713 DO_STRUCT_DIFFERENCE(Border
);
714 DO_STRUCT_DIFFERENCE(TextReset
);
715 DO_STRUCT_DIFFERENCE(Background
);
716 DO_STRUCT_DIFFERENCE(Color
);
718 #undef DO_STRUCT_DIFFERENCE
720 MOZ_ASSERT(styleStructCount
== nsStyleStructID_Length
,
721 "missing a call to DO_STRUCT_DIFFERENCE");
723 // Note that we do not check whether this->RelevantLinkVisited() !=
724 // aOther->RelevantLinkVisited(); we don't need to since
725 // nsCSSFrameConstructor::DoContentStateChanged always adds
726 // nsChangeHint_RepaintFrame for NS_EVENT_STATE_VISITED changes (and
727 // needs to, since HasStateDependentStyle probably doesn't work right
728 // for NS_EVENT_STATE_VISITED). Hopefully this doesn't actually
729 // expose whether links are visited to performance tests since all
730 // link coloring happens asynchronously at a time when it's hard for
731 // the page to measure.
732 // However, we do need to compute the larger of the changes that can
733 // happen depending on whether the link is visited or unvisited, since
734 // doing only the one that's currently appropriate would expose which
735 // links are in history to easy performance measurement. Therefore,
736 // here, we add nsChangeHint_RepaintFrame hints (the maximum for
737 // things that can depend on :visited) for the properties on which we
738 // call GetVisitedDependentColor.
739 nsStyleContext
*thisVis
= GetStyleIfVisited(),
740 *otherVis
= aOther
->GetStyleIfVisited();
741 if (!thisVis
!= !otherVis
) {
742 // One style context has a style-if-visited and the other doesn't.
743 // Presume a difference.
744 NS_UpdateHint(hint
, nsChangeHint_RepaintFrame
);
745 } else if (thisVis
&& !NS_IsHintSubset(nsChangeHint_RepaintFrame
, hint
)) {
746 // Both style contexts have a style-if-visited.
749 // NB: Calling Peek on |this|, not |thisVis|, since callers may look
750 // at a struct on |this| without looking at the same struct on
751 // |thisVis| (including this function if we skip one of these checks
752 // due to change being true already or due to the old style context
753 // not having a style-if-visited), but not the other way around.
754 if (PeekStyleColor()) {
755 if (thisVis
->StyleColor()->mColor
!=
756 otherVis
->StyleColor()->mColor
) {
761 // NB: Calling Peek on |this|, not |thisVis| (see above).
762 if (!change
&& PeekStyleBackground()) {
763 if (thisVis
->StyleBackground()->mBackgroundColor
!=
764 otherVis
->StyleBackground()->mBackgroundColor
) {
769 // NB: Calling Peek on |this|, not |thisVis| (see above).
770 if (!change
&& PeekStyleBorder()) {
771 const nsStyleBorder
*thisVisBorder
= thisVis
->StyleBorder();
772 const nsStyleBorder
*otherVisBorder
= otherVis
->StyleBorder();
773 NS_FOR_CSS_SIDES(side
) {
774 bool thisFG
, otherFG
;
775 nscolor thisColor
, otherColor
;
776 thisVisBorder
->GetBorderColor(side
, thisColor
, thisFG
);
777 otherVisBorder
->GetBorderColor(side
, otherColor
, otherFG
);
778 if (thisFG
!= otherFG
|| (!thisFG
&& thisColor
!= otherColor
)) {
785 // NB: Calling Peek on |this|, not |thisVis| (see above).
786 if (!change
&& PeekStyleOutline()) {
787 const nsStyleOutline
*thisVisOutline
= thisVis
->StyleOutline();
788 const nsStyleOutline
*otherVisOutline
= otherVis
->StyleOutline();
790 nscolor thisColor
, otherColor
;
791 if (thisVisOutline
->GetOutlineInitialColor() !=
792 otherVisOutline
->GetOutlineInitialColor() ||
793 (haveColor
= thisVisOutline
->GetOutlineColor(thisColor
)) !=
794 otherVisOutline
->GetOutlineColor(otherColor
) ||
795 (haveColor
&& thisColor
!= otherColor
)) {
800 // NB: Calling Peek on |this|, not |thisVis| (see above).
801 if (!change
&& PeekStyleColumn()) {
802 const nsStyleColumn
*thisVisColumn
= thisVis
->StyleColumn();
803 const nsStyleColumn
*otherVisColumn
= otherVis
->StyleColumn();
804 if (thisVisColumn
->mColumnRuleColor
!= otherVisColumn
->mColumnRuleColor
||
805 thisVisColumn
->mColumnRuleColorIsForeground
!=
806 otherVisColumn
->mColumnRuleColorIsForeground
) {
811 // NB: Calling Peek on |this|, not |thisVis| (see above).
812 if (!change
&& PeekStyleTextReset()) {
813 const nsStyleTextReset
*thisVisTextReset
= thisVis
->StyleTextReset();
814 const nsStyleTextReset
*otherVisTextReset
= otherVis
->StyleTextReset();
815 nscolor thisVisDecColor
, otherVisDecColor
;
816 bool thisVisDecColorIsFG
, otherVisDecColorIsFG
;
817 thisVisTextReset
->GetDecorationColor(thisVisDecColor
,
818 thisVisDecColorIsFG
);
819 otherVisTextReset
->GetDecorationColor(otherVisDecColor
,
820 otherVisDecColorIsFG
);
821 if (thisVisDecColorIsFG
!= otherVisDecColorIsFG
||
822 (!thisVisDecColorIsFG
&& thisVisDecColor
!= otherVisDecColor
)) {
827 // NB: Calling Peek on |this|, not |thisVis| (see above).
828 if (!change
&& PeekStyleSVG()) {
829 const nsStyleSVG
*thisVisSVG
= thisVis
->StyleSVG();
830 const nsStyleSVG
*otherVisSVG
= otherVis
->StyleSVG();
831 if (thisVisSVG
->mFill
!= otherVisSVG
->mFill
||
832 thisVisSVG
->mStroke
!= otherVisSVG
->mStroke
) {
838 NS_UpdateHint(hint
, nsChangeHint_RepaintFrame
);
842 return NS_SubtractHint(hint
, nsChangeHint_NeutralChange
);
846 nsStyleContext::Mark()
848 // Mark our rule node.
851 // Mark our children (i.e., tell them to mark their rule nodes, etc.).
853 nsStyleContext
* child
= mChild
;
856 child
= child
->mNextSibling
;
857 } while (mChild
!= child
);
861 nsStyleContext
* child
= mEmptyChild
;
864 child
= child
->mNextSibling
;
865 } while (mEmptyChild
!= child
);
870 void nsStyleContext::List(FILE* out
, int32_t aIndent
, bool aListDescendants
)
875 for (ix
= aIndent
; --ix
>= 0; ) {
876 str
.AppendLiteral(" ");
878 str
.Append(nsPrintfCString("%p(%d) parent=%p ",
879 (void*)this, mRefCnt
, (void *)mParent
));
882 mPseudoTag
->ToString(buffer
);
883 AppendUTF16toUTF8(buffer
, str
);
888 fprintf_stderr(out
, "%s{\n", str
.get());
890 nsRuleNode
* ruleNode
= mRuleNode
;
892 nsIStyleRule
*styleRule
= ruleNode
->GetRule();
894 styleRule
->List(out
, aIndent
+ 1);
896 ruleNode
= ruleNode
->GetParent();
898 for (ix
= aIndent
; --ix
>= 0; ) {
899 str
.AppendLiteral(" ");
901 fprintf_stderr(out
, "%s}\n", str
.get());
904 fprintf_stderr(out
, "%s{}\n", str
.get());
907 if (aListDescendants
) {
908 if (nullptr != mChild
) {
909 nsStyleContext
* child
= mChild
;
911 child
->List(out
, aIndent
+ 1, aListDescendants
);
912 child
= child
->mNextSibling
;
913 } while (mChild
!= child
);
915 if (nullptr != mEmptyChild
) {
916 nsStyleContext
* child
= mEmptyChild
;
918 child
->List(out
, aIndent
+ 1, aListDescendants
);
919 child
= child
->mNextSibling
;
920 } while (mEmptyChild
!= child
);
926 // Overloaded new operator. Initializes the memory to 0 and relies on an arena
927 // (which comes from the presShell) to perform the allocation.
929 nsStyleContext::operator new(size_t sz
, nsPresContext
* aPresContext
) CPP_THROW_NEW
931 // Check the recycle list first.
932 return aPresContext
->PresShell()->AllocateByObjectID(nsPresArena::nsStyleContext_id
, sz
);
935 // Overridden to prevent the global delete from being called, since the memory
936 // came out of an nsIArena instead of the global delete operator's heap.
938 nsStyleContext::Destroy()
940 // Get the pres context from our rule node.
941 nsRefPtr
<nsPresContext
> presContext
= mRuleNode
->PresContext();
943 // Call our destructor.
944 this->~nsStyleContext();
946 // Don't let the memory be freed, since it will be recycled
947 // instead. Don't call the global operator delete.
948 presContext
->PresShell()->FreeByObjectID(nsPresArena::nsStyleContext_id
, this);
951 already_AddRefed
<nsStyleContext
>
952 NS_NewStyleContext(nsStyleContext
* aParentContext
,
954 nsCSSPseudoElements::Type aPseudoType
,
955 nsRuleNode
* aRuleNode
,
956 bool aSkipParentDisplayBasedStyleFixup
)
958 nsRefPtr
<nsStyleContext
> context
=
959 new (aRuleNode
->PresContext())
960 nsStyleContext(aParentContext
, aPseudoTag
, aPseudoType
, aRuleNode
,
961 aSkipParentDisplayBasedStyleFixup
);
962 return context
.forget();
966 ExtractAnimationValue(nsCSSProperty aProperty
,
967 nsStyleContext
* aStyleContext
,
968 StyleAnimationValue
& aResult
)
970 DebugOnly
<bool> success
=
971 StyleAnimationValue::ExtractComputedValue(aProperty
, aStyleContext
,
973 NS_ABORT_IF_FALSE(success
,
974 "aProperty must be extractable by StyleAnimationValue");
978 ExtractColor(nsCSSProperty aProperty
,
979 nsStyleContext
*aStyleContext
)
981 StyleAnimationValue val
;
982 ExtractAnimationValue(aProperty
, aStyleContext
, val
);
983 return val
.GetColorValue();
987 ExtractColorLenient(nsCSSProperty aProperty
,
988 nsStyleContext
*aStyleContext
)
990 StyleAnimationValue val
;
991 ExtractAnimationValue(aProperty
, aStyleContext
, val
);
992 if (val
.GetUnit() == StyleAnimationValue::eUnit_Color
) {
993 return val
.GetColorValue();
995 return NS_RGBA(0, 0, 0, 0);
998 struct ColorIndexSet
{
999 uint8_t colorIndex
, alphaIndex
;
1002 static const ColorIndexSet gVisitedIndices
[2] = { { 0, 0 }, { 1, 0 } };
1005 nsStyleContext::GetVisitedDependentColor(nsCSSProperty aProperty
)
1007 NS_ASSERTION(aProperty
== eCSSProperty_color
||
1008 aProperty
== eCSSProperty_background_color
||
1009 aProperty
== eCSSProperty_border_top_color
||
1010 aProperty
== eCSSProperty_border_right_color_value
||
1011 aProperty
== eCSSProperty_border_bottom_color
||
1012 aProperty
== eCSSProperty_border_left_color_value
||
1013 aProperty
== eCSSProperty_outline_color
||
1014 aProperty
== eCSSProperty__moz_column_rule_color
||
1015 aProperty
== eCSSProperty_text_decoration_color
||
1016 aProperty
== eCSSProperty_fill
||
1017 aProperty
== eCSSProperty_stroke
,
1018 "we need to add to nsStyleContext::CalcStyleDifference");
1020 bool isPaintProperty
= aProperty
== eCSSProperty_fill
||
1021 aProperty
== eCSSProperty_stroke
;
1024 colors
[0] = isPaintProperty
? ExtractColorLenient(aProperty
, this)
1025 : ExtractColor(aProperty
, this);
1027 nsStyleContext
*visitedStyle
= this->GetStyleIfVisited();
1028 if (!visitedStyle
) {
1032 colors
[1] = isPaintProperty
? ExtractColorLenient(aProperty
, visitedStyle
)
1033 : ExtractColor(aProperty
, visitedStyle
);
1035 return nsStyleContext::CombineVisitedColors(colors
,
1036 this->RelevantLinkVisited());
1039 /* static */ nscolor
1040 nsStyleContext::CombineVisitedColors(nscolor
*aColors
, bool aLinkIsVisited
)
1042 if (NS_GET_A(aColors
[1]) == 0) {
1043 // If the style-if-visited is transparent, then just use the
1044 // unvisited style rather than using the (meaningless) color
1045 // components of the visited style along with a potentially
1046 // non-transparent alpha value.
1047 aLinkIsVisited
= false;
1050 // NOTE: We want this code to have as little timing dependence as
1051 // possible on whether this->RelevantLinkVisited() is true.
1052 const ColorIndexSet
&set
=
1053 gVisitedIndices
[aLinkIsVisited
? 1 : 0];
1055 nscolor colorColor
= aColors
[set
.colorIndex
];
1056 nscolor alphaColor
= aColors
[set
.alphaIndex
];
1057 return NS_RGBA(NS_GET_R(colorColor
), NS_GET_G(colorColor
),
1058 NS_GET_B(colorColor
), NS_GET_A(alphaColor
));
1063 nsStyleContext::AssertStyleStructMaxDifferenceValid()
1065 #define STYLE_STRUCT(name, checkdata_cb) \
1066 MOZ_ASSERT(NS_IsHintSubset(nsStyle##name::MaxDifferenceNeverInherited(), \
1067 nsStyle##name::MaxDifference()));
1068 #include "nsStyleStructList.h"
1072 /* static */ const char*
1073 nsStyleContext::StructName(nsStyleStructID aSID
)
1076 #define STYLE_STRUCT(name_, checkdata_cb) \
1077 case eStyleStruct_##name_: \
1079 #include "nsStyleStructList.h"
1087 nsStyleContext::LookupStruct(const nsACString
& aName
, nsStyleStructID
& aResult
)
1091 #define STYLE_STRUCT(name_, checkdata_cb_) \
1092 else if (aName.EqualsLiteral(#name_)) \
1093 aResult = eStyleStruct_##name_;
1094 #include "nsStyleStructList.h"
1103 nsStyleContext::HasSameCachedStyleData(nsStyleContext
* aOther
,
1104 nsStyleStructID aSID
)
1106 return GetCachedStyleData(aSID
) == aOther
->GetCachedStyleData(aSID
);
1110 nsStyleContext::SwapStyleData(nsStyleContext
* aNewContext
, uint32_t aStructs
)
1112 static_assert(nsStyleStructID_Length
<= 32, "aStructs is not big enough");
1114 for (nsStyleStructID i
= nsStyleStructID_Inherited_Start
;
1115 i
< nsStyleStructID_Inherited_Start
+ nsStyleStructID_Inherited_Count
;
1116 i
= nsStyleStructID(i
+ 1)) {
1117 uint32_t bit
= nsCachedStyleData::GetBitForSID(i
);
1118 if (!(aStructs
& bit
)) {
1121 void*& thisData
= mCachedInheritedData
.mStyleStructs
[i
];
1122 void*& otherData
= aNewContext
->mCachedInheritedData
.mStyleStructs
[i
];
1124 if (thisData
== otherData
) {
1127 } else if (!(aNewContext
->mBits
& bit
) && thisData
&& otherData
) {
1128 std::swap(thisData
, otherData
);
1132 for (nsStyleStructID i
= nsStyleStructID_Reset_Start
;
1133 i
< nsStyleStructID_Reset_Start
+ nsStyleStructID_Reset_Count
;
1134 i
= nsStyleStructID(i
+ 1)) {
1135 uint32_t bit
= nsCachedStyleData::GetBitForSID(i
);
1136 if (!(aStructs
& bit
)) {
1139 if (!mCachedResetData
) {
1140 mCachedResetData
= new (mRuleNode
->PresContext()) nsResetStyleData
;
1142 if (!aNewContext
->mCachedResetData
) {
1143 aNewContext
->mCachedResetData
=
1144 new (mRuleNode
->PresContext()) nsResetStyleData
;
1146 void*& thisData
= mCachedResetData
->mStyleStructs
[i
];
1147 void*& otherData
= aNewContext
->mCachedResetData
->mStyleStructs
[i
];
1149 if (thisData
== otherData
) {
1152 } else if (!(aNewContext
->mBits
& bit
) && thisData
&& otherData
) {
1153 std::swap(thisData
, otherData
);
1159 nsStyleContext::ClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs
)
1162 nsStyleContext
* child
= mChild
;
1164 child
->DoClearCachedInheritedStyleDataOnDescendants(aStructs
);
1165 child
= child
->mNextSibling
;
1166 } while (mChild
!= child
);
1169 nsStyleContext
* child
= mEmptyChild
;
1171 child
->DoClearCachedInheritedStyleDataOnDescendants(aStructs
);
1172 child
= child
->mNextSibling
;
1173 } while (mEmptyChild
!= child
);
1178 nsStyleContext::DoClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs
)
1180 NS_ASSERTION(mFrameRefCnt
== 0, "frame still referencing style context");
1181 for (nsStyleStructID i
= nsStyleStructID_Inherited_Start
;
1182 i
< nsStyleStructID_Inherited_Start
+ nsStyleStructID_Inherited_Count
;
1183 i
= nsStyleStructID(i
+ 1)) {
1184 uint32_t bit
= nsCachedStyleData::GetBitForSID(i
);
1185 if (aStructs
& bit
) {
1186 if (!(mBits
& bit
) && mCachedInheritedData
.mStyleStructs
[i
]) {
1189 mCachedInheritedData
.mStyleStructs
[i
] = nullptr;
1194 if (mCachedResetData
) {
1195 for (nsStyleStructID i
= nsStyleStructID_Reset_Start
;
1196 i
< nsStyleStructID_Reset_Start
+ nsStyleStructID_Reset_Count
;
1197 i
= nsStyleStructID(i
+ 1)) {
1198 uint32_t bit
= nsCachedStyleData::GetBitForSID(i
);
1199 if (aStructs
& bit
) {
1200 if (!(mBits
& bit
) && mCachedResetData
->mStyleStructs
[i
]) {
1203 mCachedResetData
->mStyleStructs
[i
] = nullptr;
1209 if (aStructs
== 0) {
1213 ClearCachedInheritedStyleDataOnDescendants(aStructs
);
1216 #ifdef RESTYLE_LOGGING
1218 nsStyleContext::GetCachedStyleDataAsString(uint32_t aStructs
)
1221 for (nsStyleStructID i
= nsStyleStructID(0);
1222 i
< nsStyleStructID_Length
;
1223 i
= nsStyleStructID(i
+ 1)) {
1224 if (aStructs
& nsCachedStyleData::GetBitForSID(i
)) {
1225 const void* data
= GetCachedStyleData(i
);
1226 if (!structs
.IsEmpty()) {
1227 structs
.Append(' ');
1229 structs
.AppendPrintf("%s=%p", StructName(i
), data
);
1230 if (HasCachedInheritedStyleData(i
)) {
1231 structs
.AppendLiteral("(dependent)");
1233 structs
.AppendLiteral("(owned)");
1241 nsStyleContext::LoggingDepth()
1243 static int32_t depth
= 0;
1248 nsStyleContext::LogStyleContextTree(int32_t aLoggingDepth
, uint32_t aStructs
)
1250 LoggingDepth() = aLoggingDepth
;
1251 LogStyleContextTree(true, aStructs
);
1255 nsStyleContext::LogStyleContextTree(bool aFirst
, uint32_t aStructs
)
1257 nsCString structs
= GetCachedStyleDataAsString(aStructs
);
1258 if (!structs
.IsEmpty()) {
1259 structs
.Append(' ');
1264 nsAutoString pseudoTag
;
1265 mPseudoTag
->ToString(pseudoTag
);
1266 AppendUTF16toUTF8(pseudoTag
, pseudo
);
1271 if (IsStyleIfVisited()) {
1272 flags
.AppendLiteral("IS_STYLE_IF_VISITED ");
1274 if (UsesGrandancestorStyle()) {
1275 flags
.AppendLiteral("USES_GRANDANCESTOR_STYLE ");
1278 flags
.AppendLiteral("IS_SHARED ");
1283 parent
.AppendPrintf("parent=%p ", mParent
);
1286 LOG_RESTYLE("%p(%d) %s%s%s%s",
1288 structs
.get(), pseudo
.get(), flags
.get(), parent
.get());
1290 LOG_RESTYLE_INDENT();
1292 if (nullptr != mChild
) {
1293 nsStyleContext
* child
= mChild
;
1295 child
->LogStyleContextTree(false, aStructs
);
1296 child
= child
->mNextSibling
;
1297 } while (mChild
!= child
);
1299 if (nullptr != mEmptyChild
) {
1300 nsStyleContext
* child
= mEmptyChild
;
1302 child
->LogStyleContextTree(false, aStructs
);
1303 child
= child
->mNextSibling
;
1304 } while (mEmptyChild
!= child
);