michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* the interface (to internal code) for retrieving computed style data */ michael@0: michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: #include "nsCSSAnonBoxes.h" michael@0: #include "nsStyleConsts.h" michael@0: #include "nsString.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsIStyleRule.h" michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsStyleSet.h" michael@0: #include "nsIPresShell.h" michael@0: michael@0: #include "nsRuleNode.h" michael@0: #include "nsStyleContext.h" michael@0: #include "nsStyleAnimation.h" michael@0: #include "GeckoProfiler.h" michael@0: michael@0: #ifdef DEBUG michael@0: // #define NOISY_DEBUG michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: michael@0: nsStyleContext::nsStyleContext(nsStyleContext* aParent, michael@0: nsIAtom* aPseudoTag, michael@0: nsCSSPseudoElements::Type aPseudoType, michael@0: nsRuleNode* aRuleNode, michael@0: bool aSkipFlexItemStyleFixup) michael@0: : mParent(aParent), michael@0: mChild(nullptr), michael@0: mEmptyChild(nullptr), michael@0: mPseudoTag(aPseudoTag), michael@0: mRuleNode(aRuleNode), michael@0: mAllocations(nullptr), michael@0: mCachedResetData(nullptr), michael@0: mBits(((uint64_t)aPseudoType) << NS_STYLE_CONTEXT_TYPE_SHIFT), michael@0: mRefCnt(0) michael@0: { michael@0: // This check has to be done "backward", because if it were written the michael@0: // more natural way it wouldn't fail even when it needed to. michael@0: static_assert((UINT64_MAX >> NS_STYLE_CONTEXT_TYPE_SHIFT) >= michael@0: nsCSSPseudoElements::ePseudo_MAX, michael@0: "pseudo element bits no longer fit in a uint64_t"); michael@0: MOZ_ASSERT(aRuleNode); michael@0: michael@0: mNextSibling = this; michael@0: mPrevSibling = this; michael@0: if (mParent) { michael@0: mParent->AddRef(); michael@0: mParent->AddChild(this); michael@0: #ifdef DEBUG michael@0: nsRuleNode *r1 = mParent->RuleNode(), *r2 = aRuleNode; michael@0: while (r1->GetParent()) michael@0: r1 = r1->GetParent(); michael@0: while (r2->GetParent()) michael@0: r2 = r2->GetParent(); michael@0: NS_ASSERTION(r1 == r2, "must be in the same rule tree as parent"); michael@0: #endif michael@0: } michael@0: michael@0: mRuleNode->AddRef(); michael@0: mRuleNode->SetUsedDirectly(); // before ApplyStyleFixups()! michael@0: michael@0: ApplyStyleFixups(aSkipFlexItemStyleFixup); michael@0: michael@0: #define eStyleStruct_LastItem (nsStyleStructID_Length - 1) michael@0: NS_ASSERTION(NS_STYLE_INHERIT_MASK & NS_STYLE_INHERIT_BIT(LastItem), michael@0: "NS_STYLE_INHERIT_MASK must be bigger, and other bits shifted"); michael@0: #undef eStyleStruct_LastItem michael@0: } michael@0: michael@0: nsStyleContext::~nsStyleContext() michael@0: { michael@0: NS_ASSERTION((nullptr == mChild) && (nullptr == mEmptyChild), "destructing context with children"); michael@0: michael@0: nsPresContext *presContext = mRuleNode->PresContext(); michael@0: michael@0: mRuleNode->Release(); michael@0: michael@0: presContext->PresShell()->StyleSet()-> michael@0: NotifyStyleContextDestroyed(presContext, this); michael@0: michael@0: if (mParent) { michael@0: mParent->RemoveChild(this); michael@0: mParent->Release(); michael@0: } michael@0: michael@0: // Free up our data structs. michael@0: mCachedInheritedData.DestroyStructs(mBits, presContext); michael@0: if (mCachedResetData) { michael@0: mCachedResetData->Destroy(mBits, presContext); michael@0: } michael@0: michael@0: FreeAllocations(presContext); michael@0: } michael@0: michael@0: void nsStyleContext::AddChild(nsStyleContext* aChild) michael@0: { michael@0: NS_ASSERTION(aChild->mPrevSibling == aChild && michael@0: aChild->mNextSibling == aChild, michael@0: "child already in a child list"); michael@0: michael@0: nsStyleContext **listPtr = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild; michael@0: // Explicitly dereference listPtr so that compiler doesn't have to know that mNextSibling michael@0: // etc. don't alias with what ever listPtr points at. michael@0: nsStyleContext *list = *listPtr; michael@0: michael@0: // Insert at the beginning of the list. See also FindChildWithRules. michael@0: if (list) { michael@0: // Link into existing elements, if there are any. michael@0: aChild->mNextSibling = list; michael@0: aChild->mPrevSibling = list->mPrevSibling; michael@0: list->mPrevSibling->mNextSibling = aChild; michael@0: list->mPrevSibling = aChild; michael@0: } michael@0: (*listPtr) = aChild; michael@0: } michael@0: michael@0: void nsStyleContext::RemoveChild(nsStyleContext* aChild) michael@0: { michael@0: NS_PRECONDITION(nullptr != aChild && this == aChild->mParent, "bad argument"); michael@0: michael@0: nsStyleContext **list = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild; michael@0: michael@0: if (aChild->mPrevSibling != aChild) { // has siblings michael@0: if ((*list) == aChild) { michael@0: (*list) = (*list)->mNextSibling; michael@0: } michael@0: } michael@0: else { michael@0: NS_ASSERTION((*list) == aChild, "bad sibling pointers"); michael@0: (*list) = nullptr; michael@0: } michael@0: michael@0: aChild->mPrevSibling->mNextSibling = aChild->mNextSibling; michael@0: aChild->mNextSibling->mPrevSibling = aChild->mPrevSibling; michael@0: aChild->mNextSibling = aChild; michael@0: aChild->mPrevSibling = aChild; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsStyleContext::FindChildWithRules(const nsIAtom* aPseudoTag, michael@0: nsRuleNode* aRuleNode, michael@0: nsRuleNode* aRulesIfVisited, michael@0: bool aRelevantLinkVisited) michael@0: { michael@0: NS_ABORT_IF_FALSE(aRulesIfVisited || !aRelevantLinkVisited, michael@0: "aRelevantLinkVisited should only be set when we have a separate style"); michael@0: uint32_t threshold = 10; // The # of siblings we're willing to examine michael@0: // before just giving this whole thing up. michael@0: michael@0: nsRefPtr result; michael@0: nsStyleContext *list = aRuleNode->IsRoot() ? mEmptyChild : mChild; michael@0: michael@0: if (list) { michael@0: nsStyleContext *child = list; michael@0: do { michael@0: if (child->mRuleNode == aRuleNode && michael@0: child->mPseudoTag == aPseudoTag && michael@0: !child->IsStyleIfVisited() && michael@0: child->RelevantLinkVisited() == aRelevantLinkVisited) { michael@0: bool match = false; michael@0: if (aRulesIfVisited) { michael@0: match = child->GetStyleIfVisited() && michael@0: child->GetStyleIfVisited()->mRuleNode == aRulesIfVisited; michael@0: } else { michael@0: match = !child->GetStyleIfVisited(); michael@0: } michael@0: if (match) { michael@0: result = child; michael@0: break; michael@0: } michael@0: } michael@0: child = child->mNextSibling; michael@0: threshold--; michael@0: if (threshold == 0) michael@0: break; michael@0: } while (child != list); michael@0: } michael@0: michael@0: if (result) { michael@0: if (result != list) { michael@0: // Move result to the front of the list. michael@0: RemoveChild(result); michael@0: AddChild(result); michael@0: } michael@0: } michael@0: michael@0: return result.forget(); michael@0: } michael@0: michael@0: const void* nsStyleContext::GetCachedStyleData(nsStyleStructID aSID) michael@0: { michael@0: const void* cachedData; michael@0: if (nsCachedStyleData::IsReset(aSID)) { michael@0: if (mCachedResetData) { michael@0: cachedData = mCachedResetData->mStyleStructs[aSID]; michael@0: } else { michael@0: cachedData = nullptr; michael@0: } michael@0: } else { michael@0: cachedData = mCachedInheritedData.mStyleStructs[aSID]; michael@0: } michael@0: return cachedData; michael@0: } michael@0: michael@0: const void* nsStyleContext::StyleData(nsStyleStructID aSID) michael@0: { michael@0: const void* cachedData = GetCachedStyleData(aSID); michael@0: if (cachedData) michael@0: return cachedData; // We have computed data stored on this node in the context tree. michael@0: return mRuleNode->GetStyleData(aSID, this, true); // Our rule node will take care of it for us. michael@0: } michael@0: michael@0: // This is an evil evil function, since it forces you to alloc your own separate copy of michael@0: // style data! Do not use this function unless you absolutely have to! You should avoid michael@0: // this at all costs! -dwh michael@0: void* michael@0: nsStyleContext::GetUniqueStyleData(const nsStyleStructID& aSID) michael@0: { michael@0: // If we already own the struct and no kids could depend on it, then michael@0: // just return it. (We leak in this case if there are kids -- and this michael@0: // function really shouldn't be called for style contexts that could michael@0: // have kids depending on the data. ClearStyleData would be OK, but michael@0: // this test for no mChild or mEmptyChild doesn't catch that case.) michael@0: const void *current = StyleData(aSID); michael@0: if (!mChild && !mEmptyChild && michael@0: !(mBits & nsCachedStyleData::GetBitForSID(aSID)) && michael@0: GetCachedStyleData(aSID)) michael@0: return const_cast(current); michael@0: michael@0: void* result; michael@0: nsPresContext *presContext = PresContext(); michael@0: switch (aSID) { michael@0: michael@0: #define UNIQUE_CASE(c_) \ michael@0: case eStyleStruct_##c_: \ michael@0: result = new (presContext) nsStyle##c_( \ michael@0: * static_cast(current)); \ michael@0: break; michael@0: michael@0: UNIQUE_CASE(Display) michael@0: UNIQUE_CASE(Background) michael@0: UNIQUE_CASE(Text) michael@0: UNIQUE_CASE(TextReset) michael@0: michael@0: #undef UNIQUE_CASE michael@0: michael@0: default: michael@0: NS_ERROR("Struct type not supported. Please find another way to do this if you can!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: SetStyle(aSID, result); michael@0: mBits &= ~static_cast(nsCachedStyleData::GetBitForSID(aSID)); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: nsStyleContext::SetStyle(nsStyleStructID aSID, void* aStruct) michael@0: { michael@0: // This method should only be called from nsRuleNode! It is not a public michael@0: // method! michael@0: michael@0: NS_ASSERTION(aSID >= 0 && aSID < nsStyleStructID_Length, "out of bounds"); michael@0: michael@0: // NOTE: nsCachedStyleData::GetStyleData works roughly the same way. michael@0: // See the comments there (in nsRuleNode.h) for more details about michael@0: // what this is doing and why. michael@0: michael@0: void** dataSlot; michael@0: if (nsCachedStyleData::IsReset(aSID)) { michael@0: if (!mCachedResetData) { michael@0: mCachedResetData = new (mRuleNode->PresContext()) nsResetStyleData; michael@0: } michael@0: dataSlot = &mCachedResetData->mStyleStructs[aSID]; michael@0: } else { michael@0: dataSlot = &mCachedInheritedData.mStyleStructs[aSID]; michael@0: } michael@0: NS_ASSERTION(!*dataSlot || (mBits & nsCachedStyleData::GetBitForSID(aSID)), michael@0: "Going to leak style data"); michael@0: *dataSlot = aStruct; michael@0: } michael@0: michael@0: void michael@0: nsStyleContext::ApplyStyleFixups(bool aSkipFlexItemStyleFixup) michael@0: { michael@0: // See if we have any text decorations. michael@0: // First see if our parent has text decorations. If our parent does, then we inherit the bit. michael@0: if (mParent && mParent->HasTextDecorationLines()) { michael@0: mBits |= NS_STYLE_HAS_TEXT_DECORATION_LINES; michael@0: } else { michael@0: // We might have defined a decoration. michael@0: const nsStyleTextReset* text = StyleTextReset(); michael@0: uint8_t decorationLine = text->mTextDecorationLine; michael@0: if (decorationLine != NS_STYLE_TEXT_DECORATION_LINE_NONE && michael@0: decorationLine != NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL) { michael@0: mBits |= NS_STYLE_HAS_TEXT_DECORATION_LINES; michael@0: } michael@0: } michael@0: michael@0: if ((mParent && mParent->HasPseudoElementData()) || mPseudoTag) { michael@0: mBits |= NS_STYLE_HAS_PSEUDO_ELEMENT_DATA; michael@0: } michael@0: michael@0: // Correct tables. michael@0: const nsStyleDisplay* disp = StyleDisplay(); michael@0: if (disp->mDisplay == NS_STYLE_DISPLAY_TABLE) { michael@0: // -moz-center and -moz-right are used for HTML's alignment michael@0: // This is covering the
...
case. michael@0: // In this case, we don't want to inherit the text alignment into the table. michael@0: const nsStyleText* text = StyleText(); michael@0: michael@0: if (text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER || michael@0: text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT) michael@0: { michael@0: nsStyleText* uniqueText = (nsStyleText*)GetUniqueStyleData(eStyleStruct_Text); michael@0: uniqueText->mTextAlign = NS_STYLE_TEXT_ALIGN_DEFAULT; michael@0: } michael@0: } michael@0: michael@0: // CSS2.1 section 9.2.4 specifies fixups for the 'display' property of michael@0: // the root element. We can't implement them in nsRuleNode because we michael@0: // don't want to store all display structs that aren't 'block', michael@0: // 'inline', or 'table' in the style context tree on the off chance michael@0: // that the root element has its style reresolved later. So do them michael@0: // here if needed, by changing the style data, so that other code michael@0: // doesn't get confused by looking at the style data. michael@0: if (!mParent) { michael@0: uint8_t displayVal = disp->mDisplay; michael@0: nsRuleNode::EnsureBlockDisplay(displayVal, true); michael@0: if (displayVal != disp->mDisplay) { michael@0: nsStyleDisplay *mutable_display = michael@0: static_cast(GetUniqueStyleData(eStyleStruct_Display)); michael@0: michael@0: // If we're in this code, then mOriginalDisplay doesn't matter michael@0: // for purposes of the cascade (because this nsStyleDisplay michael@0: // isn't living in the ruletree anyway), and for determining michael@0: // hypothetical boxes it's better to have mOriginalDisplay michael@0: // matching mDisplay here. michael@0: mutable_display->mOriginalDisplay = mutable_display->mDisplay = michael@0: displayVal; michael@0: } michael@0: } michael@0: michael@0: // Adjust the "display" values of flex and grid items (but not for raw text, michael@0: // placeholders, or table-parts). CSS3 Flexbox section 4 says: michael@0: // # The computed 'display' of a flex item is determined michael@0: // # by applying the table in CSS 2.1 Chapter 9.7. michael@0: // ...which converts inline-level elements to their block-level equivalents. michael@0: if (!aSkipFlexItemStyleFixup && mParent) { michael@0: const nsStyleDisplay* parentDisp = mParent->StyleDisplay(); michael@0: if ((parentDisp->mDisplay == NS_STYLE_DISPLAY_FLEX || michael@0: parentDisp->mDisplay == NS_STYLE_DISPLAY_INLINE_FLEX || michael@0: parentDisp->mDisplay == NS_STYLE_DISPLAY_GRID || michael@0: parentDisp->mDisplay == NS_STYLE_DISPLAY_INLINE_GRID) && michael@0: GetPseudo() != nsCSSAnonBoxes::mozNonElement) { michael@0: uint8_t displayVal = disp->mDisplay; michael@0: // Skip table parts. michael@0: // NOTE: This list needs to be kept in sync with michael@0: // nsCSSFrameConstructor.cpp's "sDisplayData" array -- specifically, michael@0: // this should be the list of display-values that have michael@0: // FCDATA_DESIRED_PARENT_TYPE_TO_BITS specified in that array. michael@0: if (NS_STYLE_DISPLAY_TABLE_CAPTION != displayVal && michael@0: NS_STYLE_DISPLAY_TABLE_ROW_GROUP != displayVal && michael@0: NS_STYLE_DISPLAY_TABLE_HEADER_GROUP != displayVal && michael@0: NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP != displayVal && michael@0: NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP != displayVal && michael@0: NS_STYLE_DISPLAY_TABLE_COLUMN != displayVal && michael@0: NS_STYLE_DISPLAY_TABLE_ROW != displayVal && michael@0: NS_STYLE_DISPLAY_TABLE_CELL != displayVal) { michael@0: michael@0: // NOTE: Technically, we shouldn't modify the 'display' value of michael@0: // positioned elements, since they aren't flex items. However, we don't michael@0: // need to worry about checking for that, because if we're positioned, michael@0: // we'll have already been through a call to EnsureBlockDisplay() in michael@0: // nsRuleNode, so this call here won't change anything. So we're OK. michael@0: nsRuleNode::EnsureBlockDisplay(displayVal); michael@0: if (displayVal != disp->mDisplay) { michael@0: NS_ASSERTION(!disp->IsAbsolutelyPositionedStyle(), michael@0: "We shouldn't be changing the display value of " michael@0: "positioned content (and we should have already " michael@0: "converted its display value to be block-level...)"); michael@0: nsStyleDisplay *mutable_display = michael@0: static_cast(GetUniqueStyleData(eStyleStruct_Display)); michael@0: mutable_display->mDisplay = displayVal; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Compute User Interface style, to trigger loads of cursors michael@0: StyleUserInterface(); michael@0: } michael@0: michael@0: nsChangeHint michael@0: nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, michael@0: nsChangeHint aParentHintsNotHandledForDescendants) michael@0: { michael@0: PROFILER_LABEL("nsStyleContext", "CalcStyleDifference"); michael@0: michael@0: NS_ABORT_IF_FALSE(NS_IsHintSubset(aParentHintsNotHandledForDescendants, michael@0: nsChangeHint_Hints_NotHandledForDescendants), michael@0: "caller is passing inherited hints, but shouldn't be"); michael@0: michael@0: nsChangeHint hint = NS_STYLE_HINT_NONE; michael@0: NS_ENSURE_TRUE(aOther, hint); michael@0: // We must always ensure that we populate the structs on the new style michael@0: // context that are filled in on the old context, so that if we get michael@0: // two style changes in succession, the second of which causes a real michael@0: // style change, the PeekStyleData doesn't return null (implying that michael@0: // nobody ever looked at that struct's data). In other words, we michael@0: // can't skip later structs if we get a big change up front, because michael@0: // we could later get a small change in one of those structs that we michael@0: // don't want to miss. michael@0: michael@0: // If our rule nodes are the same, then any differences in style data michael@0: // are already accounted for by differences on ancestors. We know michael@0: // this because CalcStyleDifference is always called on two style michael@0: // contexts that point to the same element, so we know that our michael@0: // position in the style context tree is the same and our position in michael@0: // the rule node tree is also the same. michael@0: // However, if there were noninherited style change hints on the michael@0: // parent, we might produce these same noninherited hints on this michael@0: // style context's frame due to 'inherit' values, so we do need to michael@0: // compare. michael@0: // (Things like 'em' units are handled by the change hint produced michael@0: // by font-size changing, so we don't need to worry about them like michael@0: // we worry about 'inherit' values.) michael@0: bool compare = mRuleNode != aOther->mRuleNode; michael@0: michael@0: // If we had any change in variable values, then we'll need to examine michael@0: // all of the other style structs too, even if the new style context has michael@0: // the same rule node as the old one. michael@0: const nsStyleVariables* thisVariables = PeekStyleVariables(); michael@0: if (thisVariables) { michael@0: const nsStyleVariables* otherVariables = aOther->StyleVariables(); michael@0: if (thisVariables->mVariables != otherVariables->mVariables) { michael@0: compare = true; michael@0: } michael@0: } michael@0: michael@0: DebugOnly styleStructCount = 1; // count Variables already michael@0: michael@0: #define DO_STRUCT_DIFFERENCE(struct_) \ michael@0: PR_BEGIN_MACRO \ michael@0: const nsStyle##struct_* this##struct_ = PeekStyle##struct_(); \ michael@0: if (this##struct_) { \ michael@0: const nsStyle##struct_* other##struct_ = aOther->Style##struct_(); \ michael@0: nsChangeHint maxDifference = nsStyle##struct_::MaxDifference(); \ michael@0: nsChangeHint maxDifferenceNeverInherited = \ michael@0: nsStyle##struct_::MaxDifferenceNeverInherited(); \ michael@0: if ((compare || \ michael@0: (NS_SubtractHint(maxDifference, maxDifferenceNeverInherited) & \ michael@0: aParentHintsNotHandledForDescendants)) && \ michael@0: !NS_IsHintSubset(maxDifference, hint) && \ michael@0: this##struct_ != other##struct_) { \ michael@0: NS_ASSERTION(NS_IsHintSubset( \ michael@0: this##struct_->CalcDifference(*other##struct_), \ michael@0: nsStyle##struct_::MaxDifference()), \ michael@0: "CalcDifference() returned bigger hint than MaxDifference()"); \ michael@0: NS_UpdateHint(hint, this##struct_->CalcDifference(*other##struct_)); \ michael@0: } \ michael@0: } \ michael@0: styleStructCount++; \ michael@0: PR_END_MACRO michael@0: michael@0: // In general, we want to examine structs starting with those that can michael@0: // cause the largest style change, down to those that can cause the michael@0: // smallest. This lets us skip later ones if we already have a hint michael@0: // that subsumes their MaxDifference. (As the hints get michael@0: // finer-grained, this optimization is becoming less useful, though.) michael@0: DO_STRUCT_DIFFERENCE(Display); michael@0: DO_STRUCT_DIFFERENCE(XUL); michael@0: DO_STRUCT_DIFFERENCE(Column); michael@0: DO_STRUCT_DIFFERENCE(Content); michael@0: DO_STRUCT_DIFFERENCE(UserInterface); michael@0: DO_STRUCT_DIFFERENCE(Visibility); michael@0: DO_STRUCT_DIFFERENCE(Outline); michael@0: DO_STRUCT_DIFFERENCE(TableBorder); michael@0: DO_STRUCT_DIFFERENCE(Table); michael@0: DO_STRUCT_DIFFERENCE(UIReset); michael@0: DO_STRUCT_DIFFERENCE(Text); michael@0: DO_STRUCT_DIFFERENCE(List); michael@0: DO_STRUCT_DIFFERENCE(Quotes); michael@0: DO_STRUCT_DIFFERENCE(SVGReset); michael@0: DO_STRUCT_DIFFERENCE(SVG); michael@0: DO_STRUCT_DIFFERENCE(Position); michael@0: DO_STRUCT_DIFFERENCE(Font); michael@0: DO_STRUCT_DIFFERENCE(Margin); michael@0: DO_STRUCT_DIFFERENCE(Padding); michael@0: DO_STRUCT_DIFFERENCE(Border); michael@0: DO_STRUCT_DIFFERENCE(TextReset); michael@0: DO_STRUCT_DIFFERENCE(Background); michael@0: DO_STRUCT_DIFFERENCE(Color); michael@0: michael@0: #undef DO_STRUCT_DIFFERENCE michael@0: michael@0: MOZ_ASSERT(styleStructCount == nsStyleStructID_Length, michael@0: "missing a call to DO_STRUCT_DIFFERENCE"); michael@0: michael@0: // Note that we do not check whether this->RelevantLinkVisited() != michael@0: // aOther->RelevantLinkVisited(); we don't need to since michael@0: // nsCSSFrameConstructor::DoContentStateChanged always adds michael@0: // nsChangeHint_RepaintFrame for NS_EVENT_STATE_VISITED changes (and michael@0: // needs to, since HasStateDependentStyle probably doesn't work right michael@0: // for NS_EVENT_STATE_VISITED). Hopefully this doesn't actually michael@0: // expose whether links are visited to performance tests since all michael@0: // link coloring happens asynchronously at a time when it's hard for michael@0: // the page to measure. michael@0: // However, we do need to compute the larger of the changes that can michael@0: // happen depending on whether the link is visited or unvisited, since michael@0: // doing only the one that's currently appropriate would expose which michael@0: // links are in history to easy performance measurement. Therefore, michael@0: // here, we add nsChangeHint_RepaintFrame hints (the maximum for michael@0: // things that can depend on :visited) for the properties on which we michael@0: // call GetVisitedDependentColor. michael@0: nsStyleContext *thisVis = GetStyleIfVisited(), michael@0: *otherVis = aOther->GetStyleIfVisited(); michael@0: if (!thisVis != !otherVis) { michael@0: // One style context has a style-if-visited and the other doesn't. michael@0: // Presume a difference. michael@0: NS_UpdateHint(hint, nsChangeHint_RepaintFrame); michael@0: } else if (thisVis && !NS_IsHintSubset(nsChangeHint_RepaintFrame, hint)) { michael@0: // Both style contexts have a style-if-visited. michael@0: bool change = false; michael@0: michael@0: // NB: Calling Peek on |this|, not |thisVis|, since callers may look michael@0: // at a struct on |this| without looking at the same struct on michael@0: // |thisVis| (including this function if we skip one of these checks michael@0: // due to change being true already or due to the old style context michael@0: // not having a style-if-visited), but not the other way around. michael@0: if (PeekStyleColor()) { michael@0: if (thisVis->StyleColor()->mColor != michael@0: otherVis->StyleColor()->mColor) { michael@0: change = true; michael@0: } michael@0: } michael@0: michael@0: // NB: Calling Peek on |this|, not |thisVis| (see above). michael@0: if (!change && PeekStyleBackground()) { michael@0: if (thisVis->StyleBackground()->mBackgroundColor != michael@0: otherVis->StyleBackground()->mBackgroundColor) { michael@0: change = true; michael@0: } michael@0: } michael@0: michael@0: // NB: Calling Peek on |this|, not |thisVis| (see above). michael@0: if (!change && PeekStyleBorder()) { michael@0: const nsStyleBorder *thisVisBorder = thisVis->StyleBorder(); michael@0: const nsStyleBorder *otherVisBorder = otherVis->StyleBorder(); michael@0: NS_FOR_CSS_SIDES(side) { michael@0: bool thisFG, otherFG; michael@0: nscolor thisColor, otherColor; michael@0: thisVisBorder->GetBorderColor(side, thisColor, thisFG); michael@0: otherVisBorder->GetBorderColor(side, otherColor, otherFG); michael@0: if (thisFG != otherFG || (!thisFG && thisColor != otherColor)) { michael@0: change = true; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // NB: Calling Peek on |this|, not |thisVis| (see above). michael@0: if (!change && PeekStyleOutline()) { michael@0: const nsStyleOutline *thisVisOutline = thisVis->StyleOutline(); michael@0: const nsStyleOutline *otherVisOutline = otherVis->StyleOutline(); michael@0: bool haveColor; michael@0: nscolor thisColor, otherColor; michael@0: if (thisVisOutline->GetOutlineInitialColor() != michael@0: otherVisOutline->GetOutlineInitialColor() || michael@0: (haveColor = thisVisOutline->GetOutlineColor(thisColor)) != michael@0: otherVisOutline->GetOutlineColor(otherColor) || michael@0: (haveColor && thisColor != otherColor)) { michael@0: change = true; michael@0: } michael@0: } michael@0: michael@0: // NB: Calling Peek on |this|, not |thisVis| (see above). michael@0: if (!change && PeekStyleColumn()) { michael@0: const nsStyleColumn *thisVisColumn = thisVis->StyleColumn(); michael@0: const nsStyleColumn *otherVisColumn = otherVis->StyleColumn(); michael@0: if (thisVisColumn->mColumnRuleColor != otherVisColumn->mColumnRuleColor || michael@0: thisVisColumn->mColumnRuleColorIsForeground != michael@0: otherVisColumn->mColumnRuleColorIsForeground) { michael@0: change = true; michael@0: } michael@0: } michael@0: michael@0: // NB: Calling Peek on |this|, not |thisVis| (see above). michael@0: if (!change && PeekStyleTextReset()) { michael@0: const nsStyleTextReset *thisVisTextReset = thisVis->StyleTextReset(); michael@0: const nsStyleTextReset *otherVisTextReset = otherVis->StyleTextReset(); michael@0: nscolor thisVisDecColor, otherVisDecColor; michael@0: bool thisVisDecColorIsFG, otherVisDecColorIsFG; michael@0: thisVisTextReset->GetDecorationColor(thisVisDecColor, michael@0: thisVisDecColorIsFG); michael@0: otherVisTextReset->GetDecorationColor(otherVisDecColor, michael@0: otherVisDecColorIsFG); michael@0: if (thisVisDecColorIsFG != otherVisDecColorIsFG || michael@0: (!thisVisDecColorIsFG && thisVisDecColor != otherVisDecColor)) { michael@0: change = true; michael@0: } michael@0: } michael@0: michael@0: // NB: Calling Peek on |this|, not |thisVis| (see above). michael@0: if (!change && PeekStyleSVG()) { michael@0: const nsStyleSVG *thisVisSVG = thisVis->StyleSVG(); michael@0: const nsStyleSVG *otherVisSVG = otherVis->StyleSVG(); michael@0: if (thisVisSVG->mFill != otherVisSVG->mFill || michael@0: thisVisSVG->mStroke != otherVisSVG->mStroke) { michael@0: change = true; michael@0: } michael@0: } michael@0: michael@0: if (change) { michael@0: NS_UpdateHint(hint, nsChangeHint_RepaintFrame); michael@0: } michael@0: } michael@0: michael@0: return hint; michael@0: } michael@0: michael@0: void michael@0: nsStyleContext::Mark() michael@0: { michael@0: // Mark our rule node. michael@0: mRuleNode->Mark(); michael@0: michael@0: // Mark our children (i.e., tell them to mark their rule nodes, etc.). michael@0: if (mChild) { michael@0: nsStyleContext* child = mChild; michael@0: do { michael@0: child->Mark(); michael@0: child = child->mNextSibling; michael@0: } while (mChild != child); michael@0: } michael@0: michael@0: if (mEmptyChild) { michael@0: nsStyleContext* child = mEmptyChild; michael@0: do { michael@0: child->Mark(); michael@0: child = child->mNextSibling; michael@0: } while (mEmptyChild != child); michael@0: } michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void nsStyleContext::List(FILE* out, int32_t aIndent) michael@0: { michael@0: // Indent michael@0: int32_t ix; michael@0: for (ix = aIndent; --ix >= 0; ) fputs(" ", out); michael@0: fprintf(out, "%p(%d) parent=%p ", michael@0: (void*)this, mRefCnt, (void *)mParent); michael@0: if (mPseudoTag) { michael@0: nsAutoString buffer; michael@0: mPseudoTag->ToString(buffer); michael@0: fputs(NS_LossyConvertUTF16toASCII(buffer).get(), out); michael@0: fputs(" ", out); michael@0: } michael@0: michael@0: if (mRuleNode) { michael@0: fputs("{\n", out); michael@0: nsRuleNode* ruleNode = mRuleNode; michael@0: while (ruleNode) { michael@0: nsIStyleRule *styleRule = ruleNode->GetRule(); michael@0: if (styleRule) { michael@0: styleRule->List(out, aIndent + 1); michael@0: } michael@0: ruleNode = ruleNode->GetParent(); michael@0: } michael@0: for (ix = aIndent; --ix >= 0; ) fputs(" ", out); michael@0: fputs("}\n", out); michael@0: } michael@0: else { michael@0: fputs("{}\n", out); michael@0: } michael@0: michael@0: if (nullptr != mChild) { michael@0: nsStyleContext* child = mChild; michael@0: do { michael@0: child->List(out, aIndent + 1); michael@0: child = child->mNextSibling; michael@0: } while (mChild != child); michael@0: } michael@0: if (nullptr != mEmptyChild) { michael@0: nsStyleContext* child = mEmptyChild; michael@0: do { michael@0: child->List(out, aIndent + 1); michael@0: child = child->mNextSibling; michael@0: } while (mEmptyChild != child); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: // Overloaded new operator. Initializes the memory to 0 and relies on an arena michael@0: // (which comes from the presShell) to perform the allocation. michael@0: void* michael@0: nsStyleContext::operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW michael@0: { michael@0: // Check the recycle list first. michael@0: return aPresContext->PresShell()->AllocateByObjectID(nsPresArena::nsStyleContext_id, sz); michael@0: } michael@0: michael@0: // Overridden to prevent the global delete from being called, since the memory michael@0: // came out of an nsIArena instead of the global delete operator's heap. michael@0: void michael@0: nsStyleContext::Destroy() michael@0: { michael@0: // Get the pres context from our rule node. michael@0: nsRefPtr presContext = mRuleNode->PresContext(); michael@0: michael@0: // Call our destructor. michael@0: this->~nsStyleContext(); michael@0: michael@0: // Don't let the memory be freed, since it will be recycled michael@0: // instead. Don't call the global operator delete. michael@0: presContext->PresShell()->FreeByObjectID(nsPresArena::nsStyleContext_id, this); michael@0: } michael@0: michael@0: already_AddRefed michael@0: NS_NewStyleContext(nsStyleContext* aParentContext, michael@0: nsIAtom* aPseudoTag, michael@0: nsCSSPseudoElements::Type aPseudoType, michael@0: nsRuleNode* aRuleNode, michael@0: bool aSkipFlexItemStyleFixup) michael@0: { michael@0: nsRefPtr context = michael@0: new (aRuleNode->PresContext()) michael@0: nsStyleContext(aParentContext, aPseudoTag, aPseudoType, aRuleNode, michael@0: aSkipFlexItemStyleFixup); michael@0: return context.forget(); michael@0: } michael@0: michael@0: static inline void michael@0: ExtractAnimationValue(nsCSSProperty aProperty, michael@0: nsStyleContext* aStyleContext, michael@0: nsStyleAnimation::Value& aResult) michael@0: { michael@0: DebugOnly success = michael@0: nsStyleAnimation::ExtractComputedValue(aProperty, aStyleContext, aResult); michael@0: NS_ABORT_IF_FALSE(success, michael@0: "aProperty must be extractable by nsStyleAnimation"); michael@0: } michael@0: michael@0: static nscolor michael@0: ExtractColor(nsCSSProperty aProperty, michael@0: nsStyleContext *aStyleContext) michael@0: { michael@0: nsStyleAnimation::Value val; michael@0: ExtractAnimationValue(aProperty, aStyleContext, val); michael@0: return val.GetColorValue(); michael@0: } michael@0: michael@0: static nscolor michael@0: ExtractColorLenient(nsCSSProperty aProperty, michael@0: nsStyleContext *aStyleContext) michael@0: { michael@0: nsStyleAnimation::Value val; michael@0: ExtractAnimationValue(aProperty, aStyleContext, val); michael@0: if (val.GetUnit() == nsStyleAnimation::eUnit_Color) { michael@0: return val.GetColorValue(); michael@0: } michael@0: return NS_RGBA(0, 0, 0, 0); michael@0: } michael@0: michael@0: struct ColorIndexSet { michael@0: uint8_t colorIndex, alphaIndex; michael@0: }; michael@0: michael@0: static const ColorIndexSet gVisitedIndices[2] = { { 0, 0 }, { 1, 0 } }; michael@0: michael@0: nscolor michael@0: nsStyleContext::GetVisitedDependentColor(nsCSSProperty aProperty) michael@0: { michael@0: NS_ASSERTION(aProperty == eCSSProperty_color || michael@0: aProperty == eCSSProperty_background_color || michael@0: aProperty == eCSSProperty_border_top_color || michael@0: aProperty == eCSSProperty_border_right_color_value || michael@0: aProperty == eCSSProperty_border_bottom_color || michael@0: aProperty == eCSSProperty_border_left_color_value || michael@0: aProperty == eCSSProperty_outline_color || michael@0: aProperty == eCSSProperty__moz_column_rule_color || michael@0: aProperty == eCSSProperty_text_decoration_color || michael@0: aProperty == eCSSProperty_fill || michael@0: aProperty == eCSSProperty_stroke, michael@0: "we need to add to nsStyleContext::CalcStyleDifference"); michael@0: michael@0: bool isPaintProperty = aProperty == eCSSProperty_fill || michael@0: aProperty == eCSSProperty_stroke; michael@0: michael@0: nscolor colors[2]; michael@0: colors[0] = isPaintProperty ? ExtractColorLenient(aProperty, this) michael@0: : ExtractColor(aProperty, this); michael@0: michael@0: nsStyleContext *visitedStyle = this->GetStyleIfVisited(); michael@0: if (!visitedStyle) { michael@0: return colors[0]; michael@0: } michael@0: michael@0: colors[1] = isPaintProperty ? ExtractColorLenient(aProperty, visitedStyle) michael@0: : ExtractColor(aProperty, visitedStyle); michael@0: michael@0: return nsStyleContext::CombineVisitedColors(colors, michael@0: this->RelevantLinkVisited()); michael@0: } michael@0: michael@0: /* static */ nscolor michael@0: nsStyleContext::CombineVisitedColors(nscolor *aColors, bool aLinkIsVisited) michael@0: { michael@0: if (NS_GET_A(aColors[1]) == 0) { michael@0: // If the style-if-visited is transparent, then just use the michael@0: // unvisited style rather than using the (meaningless) color michael@0: // components of the visited style along with a potentially michael@0: // non-transparent alpha value. michael@0: aLinkIsVisited = false; michael@0: } michael@0: michael@0: // NOTE: We want this code to have as little timing dependence as michael@0: // possible on whether this->RelevantLinkVisited() is true. michael@0: const ColorIndexSet &set = michael@0: gVisitedIndices[aLinkIsVisited ? 1 : 0]; michael@0: michael@0: nscolor colorColor = aColors[set.colorIndex]; michael@0: nscolor alphaColor = aColors[set.alphaIndex]; michael@0: return NS_RGBA(NS_GET_R(colorColor), NS_GET_G(colorColor), michael@0: NS_GET_B(colorColor), NS_GET_A(alphaColor)); michael@0: } michael@0: michael@0: void* michael@0: nsStyleContext::Alloc(size_t aSize) michael@0: { michael@0: nsIPresShell *shell = PresContext()->PresShell(); michael@0: michael@0: aSize += offsetof(AllocationHeader, mStorageStart); michael@0: AllocationHeader *alloc = michael@0: static_cast(shell->AllocateMisc(aSize)); michael@0: michael@0: alloc->mSize = aSize; // NOTE: inflated by header michael@0: michael@0: alloc->mNext = mAllocations; michael@0: mAllocations = alloc; michael@0: michael@0: return static_cast(&alloc->mStorageStart); michael@0: } michael@0: michael@0: void michael@0: nsStyleContext::FreeAllocations(nsPresContext *aPresContext) michael@0: { michael@0: nsIPresShell *shell = aPresContext->PresShell(); michael@0: michael@0: for (AllocationHeader *alloc = mAllocations, *next; alloc; alloc = next) { michael@0: next = alloc->mNext; michael@0: shell->FreeMisc(alloc->mSize, alloc); michael@0: } michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: /* static */ void michael@0: nsStyleContext::AssertStyleStructMaxDifferenceValid() michael@0: { michael@0: #define STYLE_STRUCT(name, checkdata_cb) \ michael@0: MOZ_ASSERT(NS_IsHintSubset(nsStyle##name::MaxDifferenceNeverInherited(), \ michael@0: nsStyle##name::MaxDifference())); michael@0: #include "nsStyleStructList.h" michael@0: #undef STYLE_STRUCT michael@0: } michael@0: #endif