summaryrefslogtreecommitdiffstats
path: root/layout/style/nsStyleContext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/nsStyleContext.cpp')
-rw-r--r--layout/style/nsStyleContext.cpp1836
1 files changed, 1836 insertions, 0 deletions
diff --git a/layout/style/nsStyleContext.cpp b/layout/style/nsStyleContext.cpp
new file mode 100644
index 000000000..7ad260f1b
--- /dev/null
+++ b/layout/style/nsStyleContext.cpp
@@ -0,0 +1,1836 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* the interface (to internal code) for retrieving computed style data */
+
+#include "CSSVariableImageTable.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Maybe.h"
+
+#include "nsCSSAnonBoxes.h"
+#include "nsCSSPseudoElements.h"
+#include "nsStyleConsts.h"
+#include "nsString.h"
+#include "nsPresContext.h"
+#include "nsIStyleRule.h"
+
+#include "nsCOMPtr.h"
+#include "nsStyleSet.h"
+#include "nsIPresShell.h"
+
+#include "nsRuleNode.h"
+#include "nsStyleContext.h"
+#include "mozilla/StyleAnimationValue.h"
+#include "GeckoProfiler.h"
+#include "nsIDocument.h"
+#include "nsPrintfCString.h"
+#include "RubyUtils.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/ArenaObjectID.h"
+#include "mozilla/StyleSetHandle.h"
+#include "mozilla/StyleSetHandleInlines.h"
+
+#include "mozilla/ReflowInput.h"
+#include "nsLayoutUtils.h"
+#include "nsCoord.h"
+
+// Ensure the binding function declarations in nsStyleContext.h matches
+// those in ServoBindings.h.
+#include "mozilla/ServoBindings.h"
+
+using namespace mozilla;
+
+//----------------------------------------------------------------------
+
+#ifdef DEBUG
+
+// Check that the style struct IDs are in the same order as they are
+// in nsStyleStructList.h, since when we set up the IDs, we include
+// the inherited and reset structs spearately from nsStyleStructList.h
+enum DebugStyleStruct {
+#define STYLE_STRUCT(name, checkdata_cb) eDebugStyleStruct_##name,
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT
+};
+
+#define STYLE_STRUCT(name, checkdata_cb) \
+ static_assert(static_cast<int>(eDebugStyleStruct_##name) == \
+ static_cast<int>(eStyleStruct_##name), \
+ "Style struct IDs are not declared in order?");
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT
+
+const uint32_t nsStyleContext::sDependencyTable[] = {
+#define STYLE_STRUCT(name, checkdata_cb)
+#define STYLE_STRUCT_DEP(dep) NS_STYLE_INHERIT_BIT(dep) |
+#define STYLE_STRUCT_END() 0,
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT
+#undef STYLE_STRUCT_DEP
+#undef STYLE_STRUCT_END
+};
+
+// Whether to perform expensive assertions in the nsStyleContext destructor.
+static bool sExpensiveStyleStructAssertionsEnabled;
+#endif
+
+nsStyleContext::nsStyleContext(nsStyleContext* aParent,
+ OwningStyleContextSource&& aSource,
+ nsIAtom* aPseudoTag,
+ CSSPseudoElementType aPseudoType)
+ : mParent(aParent)
+ , mChild(nullptr)
+ , mEmptyChild(nullptr)
+ , mPseudoTag(aPseudoTag)
+ , mSource(Move(aSource))
+#ifdef MOZ_STYLO
+ , mPresContext(nullptr)
+#endif
+ , mCachedResetData(nullptr)
+ , mBits(((uint64_t)aPseudoType) << NS_STYLE_CONTEXT_TYPE_SHIFT)
+ , mRefCnt(0)
+#ifdef MOZ_STYLO
+ , mStoredChangeHint(nsChangeHint(0))
+#ifdef DEBUG
+ , mConsumedChangeHint(false)
+#endif
+#endif
+#ifdef DEBUG
+ , mFrameRefCnt(0)
+ , mComputingStruct(nsStyleStructID_None)
+#endif
+{
+ MOZ_COUNT_CTOR(nsStyleContext);
+}
+
+nsStyleContext::nsStyleContext(nsStyleContext* aParent,
+ nsIAtom* aPseudoTag,
+ CSSPseudoElementType aPseudoType,
+ already_AddRefed<nsRuleNode> aRuleNode,
+ bool aSkipParentDisplayBasedStyleFixup)
+ : nsStyleContext(aParent, OwningStyleContextSource(Move(aRuleNode)),
+ aPseudoTag, aPseudoType)
+{
+#ifdef MOZ_STYLO
+ mPresContext = mSource.AsGeckoRuleNode()->PresContext();
+#endif
+
+ if (aParent) {
+#ifdef DEBUG
+ nsRuleNode *r1 = mParent->RuleNode(), *r2 = mSource.AsGeckoRuleNode();
+ while (r1->GetParent())
+ r1 = r1->GetParent();
+ while (r2->GetParent())
+ r2 = r2->GetParent();
+ NS_ASSERTION(r1 == r2, "must be in the same rule tree as parent");
+#endif
+ } else {
+ PresContext()->PresShell()->StyleSet()->RootStyleContextAdded();
+ }
+
+ mSource.AsGeckoRuleNode()->SetUsedDirectly(); // before ApplyStyleFixups()!
+ FinishConstruction(aSkipParentDisplayBasedStyleFixup);
+}
+
+nsStyleContext::nsStyleContext(nsStyleContext* aParent,
+ nsPresContext* aPresContext,
+ nsIAtom* aPseudoTag,
+ CSSPseudoElementType aPseudoType,
+ already_AddRefed<ServoComputedValues> aComputedValues,
+ bool aSkipParentDisplayBasedStyleFixup)
+ : nsStyleContext(aParent, OwningStyleContextSource(Move(aComputedValues)),
+ aPseudoTag, aPseudoType)
+{
+#ifdef MOZ_STYLO
+ mPresContext = aPresContext;
+#endif
+
+ FinishConstruction(aSkipParentDisplayBasedStyleFixup);
+}
+
+void
+nsStyleContext::FinishConstruction(bool aSkipParentDisplayBasedStyleFixup)
+{
+ // This check has to be done "backward", because if it were written the
+ // more natural way it wouldn't fail even when it needed to.
+ static_assert((UINT64_MAX >> NS_STYLE_CONTEXT_TYPE_SHIFT) >=
+ static_cast<CSSPseudoElementTypeBase>(
+ CSSPseudoElementType::MAX),
+ "pseudo element bits no longer fit in a uint64_t");
+ MOZ_ASSERT(!mSource.IsNull());
+
+#ifdef DEBUG
+ static_assert(MOZ_ARRAY_LENGTH(nsStyleContext::sDependencyTable)
+ == nsStyleStructID_Length,
+ "Number of items in dependency table doesn't match IDs");
+#endif
+
+ mNextSibling = this;
+ mPrevSibling = this;
+ if (mParent) {
+ mParent->AddChild(this);
+ }
+
+ SetStyleBits();
+ if (!mSource.IsServoComputedValues()) {
+ ApplyStyleFixups(aSkipParentDisplayBasedStyleFixup);
+ }
+
+ #define eStyleStruct_LastItem (nsStyleStructID_Length - 1)
+ NS_ASSERTION(NS_STYLE_INHERIT_MASK & NS_STYLE_INHERIT_BIT(LastItem),
+ "NS_STYLE_INHERIT_MASK must be bigger, and other bits shifted");
+ #undef eStyleStruct_LastItem
+}
+
+nsStyleContext::~nsStyleContext()
+{
+ MOZ_COUNT_DTOR(nsStyleContext);
+ NS_ASSERTION((nullptr == mChild) && (nullptr == mEmptyChild), "destructing context with children");
+
+#ifdef DEBUG
+ if (sExpensiveStyleStructAssertionsEnabled) {
+ // Assert that the style structs we are about to destroy are not referenced
+ // anywhere else in the style context tree. These checks are expensive,
+ // which is why they are not enabled by default.
+ nsStyleContext* root = this;
+ while (root->mParent) {
+ root = root->mParent;
+ }
+ root->AssertStructsNotUsedElsewhere(this,
+ std::numeric_limits<int32_t>::max());
+ } else {
+ // In DEBUG builds when the pref is not enabled, we perform a more limited
+ // check just of the children of this style context.
+ AssertStructsNotUsedElsewhere(this, 2);
+ }
+#endif
+
+ nsPresContext *presContext = PresContext();
+ DebugOnly<nsStyleSet*> geckoStyleSet = presContext->PresShell()->StyleSet()->GetAsGecko();
+ NS_ASSERTION(!geckoStyleSet ||
+ geckoStyleSet->GetRuleTree() == mSource.AsGeckoRuleNode()->RuleTree() ||
+ geckoStyleSet->IsInRuleTreeReconstruct(),
+ "destroying style context from old rule tree too late");
+
+ if (mParent) {
+ mParent->RemoveChild(this);
+ } else {
+ presContext->StyleSet()->RootStyleContextRemoved();
+ }
+
+ // Free up our data structs.
+ mCachedInheritedData.DestroyStructs(mBits, presContext);
+ if (mCachedResetData) {
+ mCachedResetData->Destroy(mBits, presContext);
+ }
+
+ // Free any ImageValues we were holding on to for CSS variable values.
+ CSSVariableImageTable::RemoveAll(this);
+}
+
+#ifdef DEBUG
+void
+nsStyleContext::AssertStructsNotUsedElsewhere(
+ nsStyleContext* aDestroyingContext,
+ int32_t aLevels) const
+{
+ if (aLevels == 0) {
+ return;
+ }
+
+ void* data;
+
+ if (mBits & NS_STYLE_IS_GOING_AWAY) {
+ return;
+ }
+
+ if (this != aDestroyingContext) {
+ nsInheritedStyleData& destroyingInheritedData =
+ aDestroyingContext->mCachedInheritedData;
+#define STYLE_STRUCT_INHERITED(name_, checkdata_cb) \
+ data = destroyingInheritedData.mStyleStructs[eStyleStruct_##name_]; \
+ if (data && \
+ !(aDestroyingContext->mBits & NS_STYLE_INHERIT_BIT(name_)) && \
+ (mCachedInheritedData.mStyleStructs[eStyleStruct_##name_] == data)) { \
+ printf_stderr("style struct %p found on style context %p\n", data, this);\
+ nsString url; \
+ nsresult rv = PresContext()->Document()->GetURL(url); \
+ if (NS_SUCCEEDED(rv)) { \
+ printf_stderr(" in %s\n", NS_ConvertUTF16toUTF8(url).get()); \
+ } \
+ MOZ_ASSERT(false, "destroying " #name_ " style struct still present " \
+ "in style context tree"); \
+ }
+#define STYLE_STRUCT_RESET(name_, checkdata_cb)
+
+#include "nsStyleStructList.h"
+
+#undef STYLE_STRUCT_INHERITED
+#undef STYLE_STRUCT_RESET
+
+ if (mCachedResetData) {
+ nsResetStyleData* destroyingResetData =
+ aDestroyingContext->mCachedResetData;
+ if (destroyingResetData) {
+#define STYLE_STRUCT_INHERITED(name_, checkdata_cb_)
+#define STYLE_STRUCT_RESET(name_, checkdata_cb) \
+ data = destroyingResetData->mStyleStructs[eStyleStruct_##name_]; \
+ if (data && \
+ !(aDestroyingContext->mBits & NS_STYLE_INHERIT_BIT(name_)) && \
+ (mCachedResetData->mStyleStructs[eStyleStruct_##name_] == data)) { \
+ printf_stderr("style struct %p found on style context %p\n", data, \
+ this); \
+ nsString url; \
+ nsresult rv = PresContext()->Document()->GetURL(url); \
+ if (NS_SUCCEEDED(rv)) { \
+ printf_stderr(" in %s\n", NS_ConvertUTF16toUTF8(url).get()); \
+ } \
+ MOZ_ASSERT(false, "destroying " #name_ " style struct still present "\
+ "in style context tree"); \
+ }
+
+#include "nsStyleStructList.h"
+
+#undef STYLE_STRUCT_INHERITED
+#undef STYLE_STRUCT_RESET
+ }
+ }
+ }
+
+ if (mChild) {
+ const nsStyleContext* child = mChild;
+ do {
+ child->AssertStructsNotUsedElsewhere(aDestroyingContext, aLevels - 1);
+ child = child->mNextSibling;
+ } while (child != mChild);
+ }
+
+ if (mEmptyChild) {
+ const nsStyleContext* child = mEmptyChild;
+ do {
+ child->AssertStructsNotUsedElsewhere(aDestroyingContext, aLevels - 1);
+ child = child->mNextSibling;
+ } while (child != mEmptyChild);
+ }
+}
+#endif
+
+void nsStyleContext::AddChild(nsStyleContext* aChild)
+{
+ NS_ASSERTION(aChild->mPrevSibling == aChild &&
+ aChild->mNextSibling == aChild,
+ "child already in a child list");
+
+ nsStyleContext **listPtr = aChild->mSource.MatchesNoRules() ? &mEmptyChild : &mChild;
+ // Explicitly dereference listPtr so that compiler doesn't have to know that mNextSibling
+ // etc. don't alias with what ever listPtr points at.
+ nsStyleContext *list = *listPtr;
+
+ // Insert at the beginning of the list. See also FindChildWithRules.
+ if (list) {
+ // Link into existing elements, if there are any.
+ aChild->mNextSibling = list;
+ aChild->mPrevSibling = list->mPrevSibling;
+ list->mPrevSibling->mNextSibling = aChild;
+ list->mPrevSibling = aChild;
+ }
+ (*listPtr) = aChild;
+}
+
+void nsStyleContext::RemoveChild(nsStyleContext* aChild)
+{
+ NS_PRECONDITION(nullptr != aChild && this == aChild->mParent, "bad argument");
+
+ nsStyleContext **list = aChild->mSource.MatchesNoRules() ? &mEmptyChild : &mChild;
+
+ if (aChild->mPrevSibling != aChild) { // has siblings
+ if ((*list) == aChild) {
+ (*list) = (*list)->mNextSibling;
+ }
+ }
+ else {
+ NS_ASSERTION((*list) == aChild, "bad sibling pointers");
+ (*list) = nullptr;
+ }
+
+ aChild->mPrevSibling->mNextSibling = aChild->mNextSibling;
+ aChild->mNextSibling->mPrevSibling = aChild->mPrevSibling;
+ aChild->mNextSibling = aChild;
+ aChild->mPrevSibling = aChild;
+}
+
+void
+nsStyleContext::MoveTo(nsStyleContext* aNewParent)
+{
+ MOZ_ASSERT(aNewParent != mParent);
+
+ // This function shouldn't be getting called if the parents have different
+ // values for some flags in mBits (unless the flag is also set on this style
+ // context) because if that were the case we would need to recompute those
+ // bits for |this|.
+
+#define CHECK_FLAG(bit_) \
+ MOZ_ASSERT((mParent->mBits & (bit_)) == (aNewParent->mBits & (bit_)) || \
+ (mBits & (bit_)), \
+ "MoveTo cannot be called if " #bit_ " value on old and new " \
+ "style context parents do not match, unless the flag is set " \
+ "on this style context");
+
+ CHECK_FLAG(NS_STYLE_HAS_PSEUDO_ELEMENT_DATA)
+ CHECK_FLAG(NS_STYLE_IN_DISPLAY_NONE_SUBTREE)
+ CHECK_FLAG(NS_STYLE_HAS_TEXT_DECORATION_LINES)
+ CHECK_FLAG(NS_STYLE_RELEVANT_LINK_VISITED)
+
+#undef CHECK_FLAG
+
+ // Assertions checking for visited style are just to avoid some tricky
+ // cases we can't be bothered handling at the moment.
+ MOZ_ASSERT(!IsStyleIfVisited());
+ MOZ_ASSERT(!mParent->IsStyleIfVisited());
+ MOZ_ASSERT(!aNewParent->IsStyleIfVisited());
+ MOZ_ASSERT(!mStyleIfVisited || mStyleIfVisited->mParent == mParent);
+
+ if (mParent->HasChildThatUsesResetStyle()) {
+ aNewParent->AddStyleBit(NS_STYLE_HAS_CHILD_THAT_USES_RESET_STYLE);
+ }
+
+ mParent->RemoveChild(this);
+ mParent = aNewParent;
+ mParent->AddChild(this);
+
+ if (mStyleIfVisited) {
+ mStyleIfVisited->mParent->RemoveChild(mStyleIfVisited);
+ mStyleIfVisited->mParent = aNewParent;
+ mStyleIfVisited->mParent->AddChild(mStyleIfVisited);
+ }
+}
+
+already_AddRefed<nsStyleContext>
+nsStyleContext::FindChildWithRules(const nsIAtom* aPseudoTag,
+ NonOwningStyleContextSource aSource,
+ NonOwningStyleContextSource aSourceIfVisited,
+ bool aRelevantLinkVisited)
+{
+ uint32_t threshold = 10; // The # of siblings we're willing to examine
+ // before just giving this whole thing up.
+
+ RefPtr<nsStyleContext> result;
+ nsStyleContext *list = aSource.MatchesNoRules() ? mEmptyChild : mChild;
+
+ if (list) {
+ nsStyleContext *child = list;
+ do {
+ if (child->mSource.AsRaw() == aSource &&
+ child->mPseudoTag == aPseudoTag &&
+ !child->IsStyleIfVisited() &&
+ child->RelevantLinkVisited() == aRelevantLinkVisited) {
+ bool match = false;
+ if (!aSourceIfVisited.IsNull()) {
+ match = child->GetStyleIfVisited() &&
+ child->GetStyleIfVisited()->mSource.AsRaw() == aSourceIfVisited;
+ } else {
+ match = !child->GetStyleIfVisited();
+ }
+ if (match && !(child->mBits & NS_STYLE_INELIGIBLE_FOR_SHARING)) {
+ result = child;
+ break;
+ }
+ }
+ child = child->mNextSibling;
+ threshold--;
+ if (threshold == 0)
+ break;
+ } while (child != list);
+ }
+
+ if (result) {
+ if (result != list) {
+ // Move result to the front of the list.
+ RemoveChild(result);
+ AddChild(result);
+ }
+ result->mBits |= NS_STYLE_IS_SHARED;
+ }
+
+ return result.forget();
+}
+
+const void* nsStyleContext::StyleData(nsStyleStructID aSID)
+{
+ const void* cachedData = GetCachedStyleData(aSID);
+ if (cachedData)
+ return cachedData; // We have computed data stored on this node in the context tree.
+ // Our style source will take care of it for us.
+ const void* newData;
+ if (mSource.IsGeckoRuleNode()) {
+ newData = mSource.AsGeckoRuleNode()->GetStyleData(aSID, this, true);
+ if (!nsCachedStyleData::IsReset(aSID)) {
+ // always cache inherited data on the style context; the rule
+ // node set the bit in mBits for us if needed.
+ mCachedInheritedData.mStyleStructs[aSID] = const_cast<void*>(newData);
+ }
+ } else {
+ newData = StyleStructFromServoComputedValues(aSID);
+
+ // perform any remaining main thread work on the struct
+ switch (aSID) {
+#define STYLE_STRUCT(name_, checkdata_cb_) \
+ case eStyleStruct_##name_: { \
+ auto data = static_cast<const nsStyle##name_*>(newData); \
+ const_cast<nsStyle##name_*>(data)->FinishStyle(PresContext()); \
+ break; \
+ }
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT
+ default:
+ MOZ_ASSERT_UNREACHABLE("unexpected nsStyleStructID value");
+ break;
+ }
+
+ // The Servo-backed StyleContextSource owns the struct.
+ AddStyleBit(nsCachedStyleData::GetBitForSID(aSID));
+
+ // XXXbholley: Unconditionally caching reset structs here defeats the memory
+ // optimization where we lazily allocate mCachedResetData, so that we can avoid
+ // performing an FFI call each time we want to get the style structs. We should
+ // measure the tradeoffs at some point. If the FFI overhead is low and the memory
+ // win significant, we should consider _always_ grabbing the struct over FFI, and
+ // potentially giving mCachedInheritedData the same treatment.
+ //
+ // Note that there is a similar comment in the struct getters in nsStyleContext.h.
+ SetStyle(aSID, const_cast<void*>(newData));
+ }
+ return newData;
+}
+
+// This is an evil evil function, since it forces you to alloc your own separate copy of
+// style data! Do not use this function unless you absolutely have to! You should avoid
+// this at all costs! -dwh
+void*
+nsStyleContext::GetUniqueStyleData(const nsStyleStructID& aSID)
+{
+ MOZ_ASSERT(!mSource.IsServoComputedValues(),
+ "Can't COW-mutate servo values from Gecko!");
+
+ // If we already own the struct and no kids could depend on it, then
+ // just return it. (We leak in this case if there are kids -- and this
+ // function really shouldn't be called for style contexts that could
+ // have kids depending on the data. ClearStyleData would be OK, but
+ // this test for no mChild or mEmptyChild doesn't catch that case.)
+ const void *current = StyleData(aSID);
+ if (!mChild && !mEmptyChild &&
+ !(mBits & nsCachedStyleData::GetBitForSID(aSID)) &&
+ GetCachedStyleData(aSID))
+ return const_cast<void*>(current);
+
+ void* result;
+ nsPresContext *presContext = PresContext();
+ switch (aSID) {
+
+#define UNIQUE_CASE(c_) \
+ case eStyleStruct_##c_: \
+ result = new (presContext) nsStyle##c_( \
+ * static_cast<const nsStyle##c_ *>(current)); \
+ break;
+
+ UNIQUE_CASE(Font)
+ UNIQUE_CASE(Display)
+ UNIQUE_CASE(Text)
+ UNIQUE_CASE(TextReset)
+ UNIQUE_CASE(Visibility)
+
+#undef UNIQUE_CASE
+
+ default:
+ NS_ERROR("Struct type not supported. Please find another way to do this if you can!");
+ return nullptr;
+ }
+
+ SetStyle(aSID, result);
+ mBits &= ~static_cast<uint64_t>(nsCachedStyleData::GetBitForSID(aSID));
+
+ return result;
+}
+
+// This is an evil function, but less evil than GetUniqueStyleData. It
+// creates an empty style struct for this nsStyleContext.
+void*
+nsStyleContext::CreateEmptyStyleData(const nsStyleStructID& aSID)
+{
+ MOZ_ASSERT(!mChild && !mEmptyChild &&
+ !(mBits & nsCachedStyleData::GetBitForSID(aSID)) &&
+ !GetCachedStyleData(aSID),
+ "This style should not have been computed");
+
+ void* result;
+ nsPresContext* presContext = PresContext();
+ switch (aSID) {
+#define UNIQUE_CASE(c_) \
+ case eStyleStruct_##c_: \
+ result = new (presContext) nsStyle##c_(presContext); \
+ break;
+
+ UNIQUE_CASE(Border)
+ UNIQUE_CASE(Padding)
+
+#undef UNIQUE_CASE
+
+ default:
+ NS_ERROR("Struct type not supported.");
+ return nullptr;
+ }
+
+ // The new struct is owned by this style context, but that we don't
+ // need to clear the bit in mBits because we've asserted that at the
+ // top of this function.
+ SetStyle(aSID, result);
+ return result;
+}
+
+void
+nsStyleContext::SetStyle(nsStyleStructID aSID, void* aStruct)
+{
+ // This method should only be called from nsRuleNode! It is not a public
+ // method!
+
+ NS_ASSERTION(aSID >= 0 && aSID < nsStyleStructID_Length, "out of bounds");
+
+ // NOTE: nsCachedStyleData::GetStyleData works roughly the same way.
+ // See the comments there (in nsRuleNode.h) for more details about
+ // what this is doing and why.
+
+ void** dataSlot;
+ if (nsCachedStyleData::IsReset(aSID)) {
+ if (!mCachedResetData) {
+ mCachedResetData = new (PresContext()) nsResetStyleData;
+ }
+ dataSlot = &mCachedResetData->mStyleStructs[aSID];
+ } else {
+ dataSlot = &mCachedInheritedData.mStyleStructs[aSID];
+ }
+ NS_ASSERTION(!*dataSlot || (mBits & nsCachedStyleData::GetBitForSID(aSID)),
+ "Going to leak style data");
+ *dataSlot = aStruct;
+}
+
+static bool
+ShouldSuppressLineBreak(const nsStyleContext* aContext,
+ const nsStyleDisplay* aDisplay,
+ const nsStyleContext* aParentContext,
+ const nsStyleDisplay* aParentDisplay)
+{
+ // The display change should only occur for "in-flow" children
+ if (aDisplay->IsOutOfFlowStyle()) {
+ return false;
+ }
+ // Display value of any anonymous box should not be touched. In most
+ // cases, anonymous boxes are actually not in ruby frame, but instead,
+ // some other frame with a ruby display value. Non-element pseudos
+ // which represents text frames, as well as ruby pseudos are excluded
+ // because we still want to set the flag for them.
+ if (aContext->GetPseudoType() == CSSPseudoElementType::AnonBox &&
+ !nsCSSAnonBoxes::IsNonElement(aContext->GetPseudo()) &&
+ !RubyUtils::IsRubyPseudo(aContext->GetPseudo())) {
+ return false;
+ }
+ if (aParentContext->ShouldSuppressLineBreak()) {
+ // Line break suppressing bit is propagated to any children of
+ // line participants, which include inline, contents, and inline
+ // ruby boxes.
+ if (aParentDisplay->mDisplay == mozilla::StyleDisplay::Inline ||
+ aParentDisplay->mDisplay == mozilla::StyleDisplay::Contents ||
+ aParentDisplay->mDisplay == mozilla::StyleDisplay::Ruby ||
+ aParentDisplay->mDisplay == mozilla::StyleDisplay::RubyBaseContainer) {
+ return true;
+ }
+ }
+ // Any descendant of ruby level containers is non-breakable, but
+ // the level containers themselves are breakable. We have to check
+ // the container display type against all ruby display type here
+ // because any of the ruby boxes could be anonymous.
+ // Note that, when certain HTML tags, e.g. form controls, have ruby
+ // level container display type, they could also escape from this flag
+ // while they shouldn't. However, it is generally fine since they
+ // won't usually break the assertion that there is no line break
+ // inside ruby, because:
+ // 1. their display types, the ruby level container types, are inline-
+ // outside, which means they won't cause any forced line break; and
+ // 2. they never start an inline span, which means their children, if
+ // any, won't be able to break the line its ruby ancestor lays; and
+ // 3. their parent frame is always a ruby content frame (due to
+ // anonymous ruby box generation), which makes line layout suppress
+ // any optional line break around this frame.
+ // However, there is one special case which is BR tag, because it
+ // directly affects the line layout. This case is handled by the BR
+ // frame which checks the flag of its parent frame instead of itself.
+ if ((aParentDisplay->IsRubyDisplayType() &&
+ aDisplay->mDisplay != mozilla::StyleDisplay::RubyBaseContainer &&
+ aDisplay->mDisplay != mozilla::StyleDisplay::RubyTextContainer) ||
+ // Since ruby base and ruby text may exist themselves without any
+ // non-anonymous frame outside, we should also check them.
+ aDisplay->mDisplay == mozilla::StyleDisplay::RubyBase ||
+ aDisplay->mDisplay == mozilla::StyleDisplay::RubyText) {
+ return true;
+ }
+ return false;
+}
+
+// Flex & grid containers blockify their children.
+// "The display value of a flex item is blockified"
+// https://drafts.csswg.org/css-flexbox-1/#flex-items
+// "The display value of a grid item is blockified"
+// https://drafts.csswg.org/css-grid/#grid-items
+static bool
+ShouldBlockifyChildren(const nsStyleDisplay* aStyleDisp)
+{
+ auto displayVal = aStyleDisp->mDisplay;
+ return mozilla::StyleDisplay::Flex == displayVal ||
+ mozilla::StyleDisplay::InlineFlex == displayVal ||
+ mozilla::StyleDisplay::Grid == displayVal ||
+ mozilla::StyleDisplay::InlineGrid == displayVal;
+}
+
+void
+nsStyleContext::SetStyleBits()
+{
+ // XXXbholley: We should get this information directly from the
+ // ServoComputedValues rather than computing it here. This setup for
+ // ServoComputedValues-backed nsStyleContexts is probably not something
+ // we should ship.
+ //
+ // For example, NS_STYLE_IS_TEXT_COMBINED is still set in ApplyStyleFixups,
+ // which isn't called for ServoComputedValues.
+
+ // See if we have any text decorations.
+ // First see if our parent has text decorations. If our parent does, then we inherit the bit.
+ if (mParent && mParent->HasTextDecorationLines()) {
+ mBits |= NS_STYLE_HAS_TEXT_DECORATION_LINES;
+ } else {
+ // We might have defined a decoration.
+ if (StyleTextReset()->HasTextDecorationLines()) {
+ mBits |= NS_STYLE_HAS_TEXT_DECORATION_LINES;
+ }
+ }
+
+ if ((mParent && mParent->HasPseudoElementData()) || IsPseudoElement()) {
+ mBits |= NS_STYLE_HAS_PSEUDO_ELEMENT_DATA;
+ }
+
+ // Set the NS_STYLE_IN_DISPLAY_NONE_SUBTREE bit
+ const nsStyleDisplay* disp = StyleDisplay();
+ if ((mParent && mParent->IsInDisplayNoneSubtree()) ||
+ disp->mDisplay == mozilla::StyleDisplay::None) {
+ mBits |= NS_STYLE_IN_DISPLAY_NONE_SUBTREE;
+ }
+}
+
+void
+nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup)
+{
+ MOZ_ASSERT(!mSource.IsServoComputedValues(),
+ "Can't do Gecko style fixups on Servo values");
+
+#define GET_UNIQUE_STYLE_DATA(name_) \
+ static_cast<nsStyle##name_*>(GetUniqueStyleData(eStyleStruct_##name_))
+
+ // CSS Inline Layout Level 3 - 3.5 Sizing Initial Letters:
+ // For an N-line drop initial in a Western script, the cap-height of the
+ // letter needs to be (N – 1) times the line-height, plus the cap-height
+ // of the surrounding text.
+ if (mPseudoTag == nsCSSPseudoElements::firstLetter) {
+ const nsStyleTextReset* textReset = StyleTextReset();
+ if (textReset->mInitialLetterSize != 0.0f) {
+ nsStyleContext* containerSC = mParent;
+ const nsStyleDisplay* containerDisp = containerSC->StyleDisplay();
+ while (containerDisp->mDisplay == mozilla::StyleDisplay::Contents) {
+ if (!containerSC->GetParent()) {
+ break;
+ }
+ containerSC = containerSC->GetParent();
+ containerDisp = containerSC->StyleDisplay();
+ }
+ nscoord containerLH =
+ ReflowInput::CalcLineHeight(nullptr, containerSC, NS_AUTOHEIGHT, 1.0f);
+ RefPtr<nsFontMetrics> containerFM =
+ nsLayoutUtils::GetFontMetricsForStyleContext(containerSC);
+ MOZ_ASSERT(containerFM, "Should have fontMetrics!!");
+ nscoord containerCH = containerFM->CapHeight();
+ RefPtr<nsFontMetrics> firstLetterFM =
+ nsLayoutUtils::GetFontMetricsForStyleContext(this);
+ MOZ_ASSERT(firstLetterFM, "Should have fontMetrics!!");
+ nscoord firstLetterCH = firstLetterFM->CapHeight();
+ nsStyleFont* mutableStyleFont = GET_UNIQUE_STYLE_DATA(Font);
+ float invCapHeightRatio =
+ mutableStyleFont->mFont.size / NSCoordToFloat(firstLetterCH);
+ mutableStyleFont->mFont.size =
+ NSToCoordRound(((textReset->mInitialLetterSize - 1) * containerLH +
+ containerCH) *
+ invCapHeightRatio);
+ }
+ }
+
+ // Change writing mode of text frame for text-combine-upright. We use
+ // style structs of the parent to avoid triggering computation before
+ // we change the writing mode.
+ // It is safe to look at the parent's style because we are looking at
+ // inherited properties, and ::-moz-text never matches any rules.
+ if (mPseudoTag == nsCSSAnonBoxes::mozText && mParent &&
+ mParent->StyleVisibility()->mWritingMode !=
+ NS_STYLE_WRITING_MODE_HORIZONTAL_TB &&
+ mParent->StyleText()->mTextCombineUpright ==
+ NS_STYLE_TEXT_COMBINE_UPRIGHT_ALL) {
+ MOZ_ASSERT(!PeekStyleVisibility(), "If StyleVisibility was already "
+ "computed, some properties may have been computed "
+ "incorrectly based on the old writing mode value");
+ nsStyleVisibility* mutableVis = GET_UNIQUE_STYLE_DATA(Visibility);
+ mutableVis->mWritingMode = NS_STYLE_WRITING_MODE_HORIZONTAL_TB;
+ AddStyleBit(NS_STYLE_IS_TEXT_COMBINED);
+ }
+
+ // CSS 2.1 10.1: Propagate the root element's 'direction' to the ICB.
+ // (PageContentFrame/CanvasFrame etc will inherit 'direction')
+ if (mPseudoTag == nsCSSAnonBoxes::viewport) {
+ nsPresContext* presContext = PresContext();
+ mozilla::dom::Element* docElement = presContext->Document()->GetRootElement();
+ if (docElement) {
+ RefPtr<nsStyleContext> rootStyle =
+ presContext->StyleSet()->ResolveStyleFor(docElement, nullptr);
+ auto dir = rootStyle->StyleVisibility()->mDirection;
+ if (dir != StyleVisibility()->mDirection) {
+ nsStyleVisibility* uniqueVisibility = GET_UNIQUE_STYLE_DATA(Visibility);
+ uniqueVisibility->mDirection = dir;
+ }
+ }
+ }
+
+ // Correct tables.
+ const nsStyleDisplay* disp = StyleDisplay();
+ if (disp->mDisplay == mozilla::StyleDisplay::Table) {
+ // -moz-center and -moz-right are used for HTML's alignment
+ // This is covering the <div align="right"><table>...</table></div> case.
+ // In this case, we don't want to inherit the text alignment into the table.
+ const nsStyleText* text = StyleText();
+
+ if (text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT ||
+ text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER ||
+ text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)
+ {
+ nsStyleText* uniqueText = GET_UNIQUE_STYLE_DATA(Text);
+ uniqueText->mTextAlign = NS_STYLE_TEXT_ALIGN_START;
+ }
+ }
+
+ // CSS2.1 section 9.2.4 specifies fixups for the 'display' property of
+ // the root element. We can't implement them in nsRuleNode because we
+ // don't want to store all display structs that aren't 'block',
+ // 'inline', or 'table' in the style context tree on the off chance
+ // that the root element has its style reresolved later. So do them
+ // here if needed, by changing the style data, so that other code
+ // doesn't get confused by looking at the style data.
+ if (!mParent) {
+ auto displayVal = disp->mDisplay;
+ if (displayVal != mozilla::StyleDisplay::Contents) {
+ nsRuleNode::EnsureBlockDisplay(displayVal, true);
+ } else {
+ // http://dev.w3.org/csswg/css-display/#transformations
+ // "... a display-outside of 'contents' computes to block-level
+ // on the root element."
+ displayVal = mozilla::StyleDisplay::Block;
+ }
+ if (displayVal != disp->mDisplay) {
+ nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
+ disp = mutable_display;
+
+ // If we're in this code, then mOriginalDisplay doesn't matter
+ // for purposes of the cascade (because this nsStyleDisplay
+ // isn't living in the ruletree anyway), and for determining
+ // hypothetical boxes it's better to have mOriginalDisplay
+ // matching mDisplay here.
+ mutable_display->mOriginalDisplay = mutable_display->mDisplay =
+ displayVal;
+ }
+ }
+
+ // Adjust the "display" values of flex and grid items (but not for raw text
+ // or placeholders). CSS3 Flexbox section 4 says:
+ // # The computed 'display' of a flex item is determined
+ // # by applying the table in CSS 2.1 Chapter 9.7.
+ // ...which converts inline-level elements to their block-level equivalents.
+ // Any block-level element directly contained by elements with ruby display
+ // values are converted to their inline-level equivalents.
+ if (!aSkipParentDisplayBasedStyleFixup && mParent) {
+ // Skip display:contents ancestors to reach the potential container.
+ // (If there are only display:contents ancestors between this node and
+ // a flex/grid container ancestor, then this node is a flex/grid item, since
+ // its parent *in the frame tree* will be the flex/grid container. So we treat
+ // it like a flex/grid item here.)
+ nsStyleContext* containerContext = mParent;
+ const nsStyleDisplay* containerDisp = containerContext->StyleDisplay();
+ while (containerDisp->mDisplay == mozilla::StyleDisplay::Contents) {
+ if (!containerContext->GetParent()) {
+ break;
+ }
+ containerContext = containerContext->GetParent();
+ containerDisp = containerContext->StyleDisplay();
+ }
+ if (ShouldBlockifyChildren(containerDisp) &&
+ !nsCSSAnonBoxes::IsNonElement(GetPseudo())) {
+ // NOTE: Technically, we shouldn't modify the 'display' value of
+ // positioned elements, since they aren't flex/grid items. However,
+ // we don't need to worry about checking for that, because if we're
+ // positioned, we'll have already been through a call to
+ // EnsureBlockDisplay() in nsRuleNode, so this call here won't change
+ // anything. So we're OK.
+ auto displayVal = disp->mDisplay;
+ nsRuleNode::EnsureBlockDisplay(displayVal);
+ if (displayVal != disp->mDisplay) {
+ NS_ASSERTION(!disp->IsAbsolutelyPositionedStyle(),
+ "We shouldn't be changing the display value of "
+ "positioned content (and we should have already "
+ "converted its display value to be block-level...)");
+ nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
+ disp = mutable_display;
+ mutable_display->mDisplay = displayVal;
+ }
+ }
+ }
+
+ // Note: This must come after the blockification above, otherwise we fail
+ // the grid-item-blockifying-001.html reftest.
+ if (mParent && ::ShouldSuppressLineBreak(this, disp, mParent,
+ mParent->StyleDisplay())) {
+ mBits |= NS_STYLE_SUPPRESS_LINEBREAK;
+ auto displayVal = disp->mDisplay;
+ nsRuleNode::EnsureInlineDisplay(displayVal);
+ if (displayVal != disp->mDisplay) {
+ nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
+ disp = mutable_display;
+ mutable_display->mDisplay = displayVal;
+ }
+ }
+ // Suppress border/padding of ruby level containers
+ if (disp->mDisplay == mozilla::StyleDisplay::RubyBaseContainer ||
+ disp->mDisplay == mozilla::StyleDisplay::RubyTextContainer) {
+ CreateEmptyStyleData(eStyleStruct_Border);
+ CreateEmptyStyleData(eStyleStruct_Padding);
+ }
+ if (disp->IsRubyDisplayType()) {
+ // Per CSS Ruby spec section Bidi Reordering, for all ruby boxes,
+ // the 'normal' and 'embed' values of 'unicode-bidi' should compute to
+ // 'isolate', and 'bidi-override' should compute to 'isolate-override'.
+ const nsStyleTextReset* textReset = StyleTextReset();
+ uint8_t unicodeBidi = textReset->mUnicodeBidi;
+ if (unicodeBidi == NS_STYLE_UNICODE_BIDI_NORMAL ||
+ unicodeBidi == NS_STYLE_UNICODE_BIDI_EMBED) {
+ unicodeBidi = NS_STYLE_UNICODE_BIDI_ISOLATE;
+ } else if (unicodeBidi == NS_STYLE_UNICODE_BIDI_BIDI_OVERRIDE) {
+ unicodeBidi = NS_STYLE_UNICODE_BIDI_ISOLATE_OVERRIDE;
+ }
+ if (unicodeBidi != textReset->mUnicodeBidi) {
+ nsStyleTextReset* mutableTextReset = GET_UNIQUE_STYLE_DATA(TextReset);
+ mutableTextReset->mUnicodeBidi = unicodeBidi;
+ }
+ }
+
+ /*
+ * According to https://drafts.csswg.org/css-writing-modes-3/#block-flow:
+ *
+ * If a box has a different block flow direction than its containing block:
+ * * If the box has a specified display of inline, its display computes
+ * to inline-block. [CSS21]
+ * ...etc.
+ */
+ if (disp->mDisplay == mozilla::StyleDisplay::Inline &&
+ !nsCSSAnonBoxes::IsNonElement(mPseudoTag) &&
+ mParent) {
+ auto cbContext = mParent;
+ while (cbContext->StyleDisplay()->mDisplay == mozilla::StyleDisplay::Contents) {
+ cbContext = cbContext->mParent;
+ }
+ MOZ_ASSERT(cbContext, "the root context can't have display:contents");
+ // We don't need the full mozilla::WritingMode value (incorporating dir
+ // and text-orientation) here; just the writing-mode property is enough.
+ if (StyleVisibility()->mWritingMode !=
+ cbContext->StyleVisibility()->mWritingMode) {
+ nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
+ disp = mutable_display;
+ mutable_display->mOriginalDisplay = mutable_display->mDisplay =
+ mozilla::StyleDisplay::InlineBlock;
+ }
+ }
+
+ // Compute User Interface style, to trigger loads of cursors
+ StyleUserInterface();
+#undef GET_UNIQUE_STYLE_DATA
+}
+
+template<class StyleContextLike>
+nsChangeHint
+nsStyleContext::CalcStyleDifferenceInternal(StyleContextLike* aNewContext,
+ nsChangeHint aParentHintsNotHandledForDescendants,
+ uint32_t* aEqualStructs,
+ uint32_t* aSamePointerStructs)
+{
+ PROFILER_LABEL("nsStyleContext", "CalcStyleDifference",
+ js::ProfileEntry::Category::CSS);
+
+ MOZ_ASSERT(NS_IsHintSubset(aParentHintsNotHandledForDescendants,
+ nsChangeHint_Hints_NotHandledForDescendants),
+ "caller is passing inherited hints, but shouldn't be");
+
+ static_assert(nsStyleStructID_Length <= 32,
+ "aEqualStructs is not big enough");
+
+ *aEqualStructs = 0;
+
+ nsChangeHint hint = nsChangeHint(0);
+ NS_ENSURE_TRUE(aNewContext, hint);
+ // We must always ensure that we populate the structs on the new style
+ // context that are filled in on the old context, so that if we get
+ // two style changes in succession, the second of which causes a real
+ // style change, the PeekStyleData doesn't return null (implying that
+ // nobody ever looked at that struct's data). In other words, we
+ // can't skip later structs if we get a big change up front, because
+ // we could later get a small change in one of those structs that we
+ // don't want to miss.
+
+ // If our sources are the same, then any differences in style data
+ // are already accounted for by differences on ancestors. We know
+ // this because CalcStyleDifference is always called on two style
+ // contexts that point to the same element, so we know that our
+ // position in the style context tree is the same and our position in
+ // the rule node tree (if applicable) is also the same.
+ // However, if there were noninherited style change hints on the
+ // parent, we might produce these same noninherited hints on this
+ // style context's frame due to 'inherit' values, so we do need to
+ // compare.
+ // (Things like 'em' units are handled by the change hint produced
+ // by font-size changing, so we don't need to worry about them like
+ // we worry about 'inherit' values.)
+ bool compare = StyleSource() != aNewContext->StyleSource();
+
+ DebugOnly<uint32_t> structsFound = 0;
+
+ // If we had any change in variable values, then we'll need to examine
+ // all of the other style structs too, even if the new style context has
+ // the same source as the old one.
+ const nsStyleVariables* thisVariables = PeekStyleVariables();
+ if (thisVariables) {
+ structsFound |= NS_STYLE_INHERIT_BIT(Variables);
+ const nsStyleVariables* otherVariables = aNewContext->StyleVariables();
+ if (thisVariables->mVariables == otherVariables->mVariables) {
+ *aEqualStructs |= NS_STYLE_INHERIT_BIT(Variables);
+ } else {
+ compare = true;
+ }
+ } else {
+ *aEqualStructs |= NS_STYLE_INHERIT_BIT(Variables);
+ }
+
+ DebugOnly<int> styleStructCount = 1; // count Variables already
+
+#define DO_STRUCT_DIFFERENCE(struct_) \
+ PR_BEGIN_MACRO \
+ const nsStyle##struct_* this##struct_ = PeekStyle##struct_(); \
+ if (this##struct_) { \
+ structsFound |= NS_STYLE_INHERIT_BIT(struct_); \
+ const nsStyle##struct_* other##struct_ = aNewContext->Style##struct_(); \
+ nsChangeHint maxDifference = nsStyle##struct_::MaxDifference(); \
+ nsChangeHint differenceAlwaysHandledForDescendants = \
+ nsStyle##struct_::DifferenceAlwaysHandledForDescendants(); \
+ if (this##struct_ == other##struct_) { \
+ /* The very same struct, so we know that there will be no */ \
+ /* differences. */ \
+ *aEqualStructs |= NS_STYLE_INHERIT_BIT(struct_); \
+ } else if (compare || \
+ ((maxDifference & ~differenceAlwaysHandledForDescendants) & \
+ aParentHintsNotHandledForDescendants)) { \
+ nsChangeHint difference = \
+ this##struct_->CalcDifference(*other##struct_ EXTRA_DIFF_ARGS); \
+ NS_ASSERTION(NS_IsHintSubset(difference, maxDifference), \
+ "CalcDifference() returned bigger hint than " \
+ "MaxDifference()"); \
+ hint |= difference; \
+ if (!difference) { \
+ *aEqualStructs |= NS_STYLE_INHERIT_BIT(struct_); \
+ } \
+ } else { \
+ /* We still must call CalcDifference to see if there were any */ \
+ /* changes so that we can set *aEqualStructs appropriately. */ \
+ nsChangeHint difference = \
+ this##struct_->CalcDifference(*other##struct_ EXTRA_DIFF_ARGS); \
+ NS_ASSERTION(NS_IsHintSubset(difference, maxDifference), \
+ "CalcDifference() returned bigger hint than " \
+ "MaxDifference()"); \
+ if (!difference) { \
+ *aEqualStructs |= NS_STYLE_INHERIT_BIT(struct_); \
+ } \
+ } \
+ } else { \
+ *aEqualStructs |= NS_STYLE_INHERIT_BIT(struct_); \
+ } \
+ styleStructCount++; \
+ PR_END_MACRO
+
+ // In general, we want to examine structs starting with those that can
+ // cause the largest style change, down to those that can cause the
+ // smallest. This lets us skip later ones if we already have a hint
+ // that subsumes their MaxDifference. (As the hints get
+ // finer-grained, this optimization is becoming less useful, though.)
+#define EXTRA_DIFF_ARGS /* nothing */
+ DO_STRUCT_DIFFERENCE(Display);
+ DO_STRUCT_DIFFERENCE(XUL);
+ DO_STRUCT_DIFFERENCE(Column);
+ DO_STRUCT_DIFFERENCE(Content);
+ DO_STRUCT_DIFFERENCE(UserInterface);
+ DO_STRUCT_DIFFERENCE(Visibility);
+ DO_STRUCT_DIFFERENCE(Outline);
+ DO_STRUCT_DIFFERENCE(TableBorder);
+ DO_STRUCT_DIFFERENCE(Table);
+ DO_STRUCT_DIFFERENCE(UIReset);
+ DO_STRUCT_DIFFERENCE(Text);
+ DO_STRUCT_DIFFERENCE(List);
+ DO_STRUCT_DIFFERENCE(SVGReset);
+ DO_STRUCT_DIFFERENCE(SVG);
+#undef EXTRA_DIFF_ARGS
+#define EXTRA_DIFF_ARGS , PeekStyleVisibility()
+ DO_STRUCT_DIFFERENCE(Position);
+#undef EXTRA_DIFF_ARGS
+#define EXTRA_DIFF_ARGS /* nothing */
+ DO_STRUCT_DIFFERENCE(Font);
+ DO_STRUCT_DIFFERENCE(Margin);
+ DO_STRUCT_DIFFERENCE(Padding);
+ DO_STRUCT_DIFFERENCE(Border);
+ DO_STRUCT_DIFFERENCE(TextReset);
+ DO_STRUCT_DIFFERENCE(Effects);
+ DO_STRUCT_DIFFERENCE(Background);
+ DO_STRUCT_DIFFERENCE(Color);
+#undef EXTRA_DIFF_ARGS
+
+#undef DO_STRUCT_DIFFERENCE
+
+ MOZ_ASSERT(styleStructCount == nsStyleStructID_Length,
+ "missing a call to DO_STRUCT_DIFFERENCE");
+
+#ifdef DEBUG
+ #define STYLE_STRUCT(name_, callback_) \
+ MOZ_ASSERT(!!(structsFound & NS_STYLE_INHERIT_BIT(name_)) == \
+ !!PeekStyle##name_(), \
+ "PeekStyleData results must not change in the middle of " \
+ "difference calculation.");
+ #include "nsStyleStructList.h"
+ #undef STYLE_STRUCT
+#endif
+
+ // We check for struct pointer equality here rather than as part of the
+ // DO_STRUCT_DIFFERENCE calls, since those calls can result in structs
+ // we previously examined and found to be null on this style context
+ // getting computed by later DO_STRUCT_DIFFERENCE calls (which can
+ // happen when the nsRuleNode::ComputeXXXData method looks up another
+ // struct.) This is important for callers in RestyleManager that
+ // need to know the equality or not of the final set of cached struct
+ // pointers.
+ *aSamePointerStructs = 0;
+
+#define STYLE_STRUCT(name_, callback_) \
+ { \
+ const nsStyle##name_* data = PeekStyle##name_(); \
+ if (!data || data == aNewContext->Style##name_()) { \
+ *aSamePointerStructs |= NS_STYLE_INHERIT_BIT(name_); \
+ } \
+ }
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT
+
+ // Note that we do not check whether this->RelevantLinkVisited() !=
+ // aNewContext->RelevantLinkVisited(); we don't need to since
+ // nsCSSFrameConstructor::DoContentStateChanged always adds
+ // nsChangeHint_RepaintFrame for NS_EVENT_STATE_VISITED changes (and
+ // needs to, since HasStateDependentStyle probably doesn't work right
+ // for NS_EVENT_STATE_VISITED). Hopefully this doesn't actually
+ // expose whether links are visited to performance tests since all
+ // link coloring happens asynchronously at a time when it's hard for
+ // the page to measure.
+ // However, we do need to compute the larger of the changes that can
+ // happen depending on whether the link is visited or unvisited, since
+ // doing only the one that's currently appropriate would expose which
+ // links are in history to easy performance measurement. Therefore,
+ // here, we add nsChangeHint_RepaintFrame hints (the maximum for
+ // things that can depend on :visited) for the properties on which we
+ // call GetVisitedDependentColor.
+ nsStyleContext *thisVis = GetStyleIfVisited(),
+ *otherVis = aNewContext->GetStyleIfVisited();
+ if (!thisVis != !otherVis) {
+ // One style context has a style-if-visited and the other doesn't.
+ // Presume a difference.
+ hint |= nsChangeHint_RepaintFrame;
+ } else if (thisVis && !NS_IsHintSubset(nsChangeHint_RepaintFrame, hint)) {
+ // Both style contexts have a style-if-visited.
+ bool change = false;
+
+ // NB: Calling Peek on |this|, not |thisVis|, since callers may look
+ // at a struct on |this| without looking at the same struct on
+ // |thisVis| (including this function if we skip one of these checks
+ // due to change being true already or due to the old style context
+ // not having a style-if-visited), but not the other way around.
+ if (PeekStyleColor()) {
+ if (thisVis->StyleColor()->mColor !=
+ otherVis->StyleColor()->mColor) {
+ change = true;
+ }
+ }
+
+ // NB: Calling Peek on |this|, not |thisVis| (see above).
+ if (!change && PeekStyleBackground()) {
+ if (thisVis->StyleBackground()->mBackgroundColor !=
+ otherVis->StyleBackground()->mBackgroundColor) {
+ change = true;
+ }
+ }
+
+ // NB: Calling Peek on |this|, not |thisVis| (see above).
+ if (!change && PeekStyleBorder()) {
+ const nsStyleBorder *thisVisBorder = thisVis->StyleBorder();
+ const nsStyleBorder *otherVisBorder = otherVis->StyleBorder();
+ NS_FOR_CSS_SIDES(side) {
+ if (thisVisBorder->mBorderColor[side] !=
+ otherVisBorder->mBorderColor[side]) {
+ change = true;
+ break;
+ }
+ }
+ }
+
+ // NB: Calling Peek on |this|, not |thisVis| (see above).
+ if (!change && PeekStyleOutline()) {
+ const nsStyleOutline *thisVisOutline = thisVis->StyleOutline();
+ const nsStyleOutline *otherVisOutline = otherVis->StyleOutline();
+ if (thisVisOutline->mOutlineColor != otherVisOutline->mOutlineColor) {
+ change = true;
+ }
+ }
+
+ // NB: Calling Peek on |this|, not |thisVis| (see above).
+ if (!change && PeekStyleColumn()) {
+ const nsStyleColumn *thisVisColumn = thisVis->StyleColumn();
+ const nsStyleColumn *otherVisColumn = otherVis->StyleColumn();
+ if (thisVisColumn->mColumnRuleColor != otherVisColumn->mColumnRuleColor) {
+ change = true;
+ }
+ }
+
+ // NB: Calling Peek on |this|, not |thisVis| (see above).
+ if (!change && PeekStyleText()) {
+ const nsStyleText* thisVisText = thisVis->StyleText();
+ const nsStyleText* otherVisText = otherVis->StyleText();
+ if (thisVisText->mTextEmphasisColor != otherVisText->mTextEmphasisColor ||
+ thisVisText->mWebkitTextFillColor != otherVisText->mWebkitTextFillColor ||
+ thisVisText->mWebkitTextStrokeColor != otherVisText->mWebkitTextStrokeColor) {
+ change = true;
+ }
+ }
+
+ // NB: Calling Peek on |this|, not |thisVis| (see above).
+ if (!change && PeekStyleTextReset()) {
+ const nsStyleTextReset *thisVisTextReset = thisVis->StyleTextReset();
+ const nsStyleTextReset *otherVisTextReset = otherVis->StyleTextReset();
+ if (thisVisTextReset->mTextDecorationColor !=
+ otherVisTextReset->mTextDecorationColor) {
+ change = true;
+ }
+ }
+
+ // NB: Calling Peek on |this|, not |thisVis| (see above).
+ if (!change && PeekStyleSVG()) {
+ const nsStyleSVG *thisVisSVG = thisVis->StyleSVG();
+ const nsStyleSVG *otherVisSVG = otherVis->StyleSVG();
+ if (thisVisSVG->mFill != otherVisSVG->mFill ||
+ thisVisSVG->mStroke != otherVisSVG->mStroke) {
+ change = true;
+ }
+ }
+
+ if (change) {
+ hint |= nsChangeHint_RepaintFrame;
+ }
+ }
+
+ if (hint & nsChangeHint_UpdateContainingBlock) {
+ // If a struct returned nsChangeHint_UpdateContainingBlock, that
+ // means that one property's influence on whether we're a containing
+ // block for abs-pos or fixed-pos elements has changed. However, we
+ // only need to return the hint if the overall computation of
+ // whether we establish a containing block has changed.
+
+ // This depends on data in nsStyleDisplay and nsStyleEffects, so we
+ // do it here.
+
+ // Note that it's perhaps good for this test to be last because it
+ // doesn't use Peek* functions to get the structs on the old
+ // context. But this isn't a big concern because these struct
+ // getters should be called during frame construction anyway.
+ if (StyleDisplay()->IsAbsPosContainingBlockForAppropriateFrame(this) ==
+ aNewContext->StyleDisplay()->
+ IsAbsPosContainingBlockForAppropriateFrame(aNewContext) &&
+ StyleDisplay()->IsFixedPosContainingBlockForAppropriateFrame(this) ==
+ aNewContext->StyleDisplay()->
+ IsFixedPosContainingBlockForAppropriateFrame(aNewContext)) {
+ // While some styles that cause the frame to be a containing block
+ // has changed, the overall result hasn't.
+ hint &= ~nsChangeHint_UpdateContainingBlock;
+ }
+ }
+
+ MOZ_ASSERT(NS_IsHintSubset(hint, nsChangeHint_AllHints),
+ "Added a new hint without bumping AllHints?");
+ return hint & ~nsChangeHint_NeutralChange;
+}
+
+nsChangeHint
+nsStyleContext::CalcStyleDifference(nsStyleContext* aNewContext,
+ nsChangeHint aParentHintsNotHandledForDescendants,
+ uint32_t* aEqualStructs,
+ uint32_t* aSamePointerStructs)
+{
+ return CalcStyleDifferenceInternal(aNewContext, aParentHintsNotHandledForDescendants,
+ aEqualStructs, aSamePointerStructs);
+}
+
+class MOZ_STACK_CLASS FakeStyleContext
+{
+public:
+ explicit FakeStyleContext(const ServoComputedValues* aComputedValues)
+ : mComputedValues(aComputedValues) {}
+
+ mozilla::NonOwningStyleContextSource StyleSource() const {
+ return mozilla::NonOwningStyleContextSource(mComputedValues);
+ }
+
+ nsStyleContext* GetStyleIfVisited() {
+ // XXXbholley: This is wrong. Need to implement to get visited handling
+ // corrrect!
+ return nullptr;
+ }
+
+ #define STYLE_STRUCT(name_, checkdata_cb_) \
+ const nsStyle##name_ * Style##name_() { \
+ return Servo_GetStyle##name_(mComputedValues); \
+ }
+ #include "nsStyleStructList.h"
+ #undef STYLE_STRUCT
+
+private:
+ const ServoComputedValues* MOZ_NON_OWNING_REF mComputedValues;
+};
+
+nsChangeHint
+nsStyleContext::CalcStyleDifference(const ServoComputedValues* aNewComputedValues,
+ nsChangeHint aParentHintsNotHandledForDescendants,
+ uint32_t* aEqualStructs,
+ uint32_t* aSamePointerStructs)
+{
+ FakeStyleContext newContext(aNewComputedValues);
+ return CalcStyleDifferenceInternal(&newContext, aParentHintsNotHandledForDescendants,
+ aEqualStructs, aSamePointerStructs);
+}
+
+#ifdef DEBUG
+void nsStyleContext::List(FILE* out, int32_t aIndent, bool aListDescendants)
+{
+ nsAutoCString str;
+ // Indent
+ int32_t ix;
+ for (ix = aIndent; --ix >= 0; ) {
+ str.AppendLiteral(" ");
+ }
+ str.Append(nsPrintfCString("%p(%d) parent=%p ",
+ (void*)this, mRefCnt, (void *)mParent));
+ if (mPseudoTag) {
+ nsAutoString buffer;
+ mPseudoTag->ToString(buffer);
+ AppendUTF16toUTF8(buffer, str);
+ str.Append(' ');
+ }
+
+ if (mSource.IsServoComputedValues()) {
+ fprintf_stderr(out, "%s{ServoComputedValues}\n", str.get());
+ } else if (mSource.IsGeckoRuleNode()) {
+ fprintf_stderr(out, "%s{\n", str.get());
+ str.Truncate();
+ nsRuleNode* ruleNode = mSource.AsGeckoRuleNode();
+ while (ruleNode) {
+ nsIStyleRule *styleRule = ruleNode->GetRule();
+ if (styleRule) {
+ styleRule->List(out, aIndent + 1);
+ }
+ ruleNode = ruleNode->GetParent();
+ }
+ for (ix = aIndent; --ix >= 0; ) {
+ str.AppendLiteral(" ");
+ }
+ fprintf_stderr(out, "%s}\n", str.get());
+ }
+ else {
+ fprintf_stderr(out, "%s{}\n", str.get());
+ }
+
+ if (aListDescendants) {
+ if (nullptr != mChild) {
+ nsStyleContext* child = mChild;
+ do {
+ child->List(out, aIndent + 1, aListDescendants);
+ child = child->mNextSibling;
+ } while (mChild != child);
+ }
+ if (nullptr != mEmptyChild) {
+ nsStyleContext* child = mEmptyChild;
+ do {
+ child->List(out, aIndent + 1, aListDescendants);
+ child = child->mNextSibling;
+ } while (mEmptyChild != child);
+ }
+ }
+}
+#endif
+
+// Overloaded new operator. Initializes the memory to 0 and relies on an arena
+// (which comes from the presShell) to perform the allocation.
+void*
+nsStyleContext::operator new(size_t sz, nsPresContext* aPresContext)
+{
+ // Check the recycle list first.
+ return aPresContext->PresShell()->
+ AllocateByObjectID(eArenaObjectID_nsStyleContext, sz);
+}
+
+// Overridden to prevent the global delete from being called, since the memory
+// came out of an nsIArena instead of the global delete operator's heap.
+void
+nsStyleContext::Destroy()
+{
+ // Get the pres context.
+ RefPtr<nsPresContext> presContext = PresContext();
+
+ // Call our destructor.
+ this->~nsStyleContext();
+
+ // Don't let the memory be freed, since it will be recycled
+ // instead. Don't call the global operator delete.
+ presContext->PresShell()->
+ FreeByObjectID(eArenaObjectID_nsStyleContext, this);
+}
+
+already_AddRefed<nsStyleContext>
+NS_NewStyleContext(nsStyleContext* aParentContext,
+ nsIAtom* aPseudoTag,
+ CSSPseudoElementType aPseudoType,
+ nsRuleNode* aRuleNode,
+ bool aSkipParentDisplayBasedStyleFixup)
+{
+ RefPtr<nsRuleNode> node = aRuleNode;
+ RefPtr<nsStyleContext> context =
+ new (aRuleNode->PresContext())
+ nsStyleContext(aParentContext, aPseudoTag, aPseudoType, node.forget(),
+ aSkipParentDisplayBasedStyleFixup);
+ return context.forget();
+}
+
+already_AddRefed<nsStyleContext>
+NS_NewStyleContext(nsStyleContext* aParentContext,
+ nsPresContext* aPresContext,
+ nsIAtom* aPseudoTag,
+ CSSPseudoElementType aPseudoType,
+ already_AddRefed<ServoComputedValues> aComputedValues,
+ bool aSkipParentDisplayBasedStyleFixup)
+{
+ RefPtr<nsStyleContext> context =
+ new (aPresContext)
+ nsStyleContext(aParentContext, aPresContext, aPseudoTag, aPseudoType,
+ Move(aComputedValues), aSkipParentDisplayBasedStyleFixup);
+ return context.forget();
+}
+
+nsIPresShell*
+nsStyleContext::Arena()
+{
+ return PresContext()->PresShell();
+}
+
+static inline void
+ExtractAnimationValue(nsCSSPropertyID aProperty,
+ nsStyleContext* aStyleContext,
+ StyleAnimationValue& aResult)
+{
+ DebugOnly<bool> success =
+ StyleAnimationValue::ExtractComputedValue(aProperty, aStyleContext,
+ aResult);
+ MOZ_ASSERT(success,
+ "aProperty must be extractable by StyleAnimationValue");
+}
+
+static Maybe<nscolor>
+ExtractColor(nsCSSPropertyID aProperty,
+ nsStyleContext *aStyleContext)
+{
+ StyleAnimationValue val;
+ ExtractAnimationValue(aProperty, aStyleContext, val);
+ switch (val.GetUnit()) {
+ case StyleAnimationValue::eUnit_Color:
+ return Some(val.GetCSSValueValue()->GetColorValue());
+ case StyleAnimationValue::eUnit_CurrentColor:
+ return Some(aStyleContext->StyleColor()->mColor);
+ case StyleAnimationValue::eUnit_ComplexColor:
+ return Some(aStyleContext->StyleColor()->
+ CalcComplexColor(val.GetStyleComplexColorValue()));
+ default:
+ return Nothing();
+ }
+}
+
+static nscolor
+ExtractColorLenient(nsCSSPropertyID aProperty,
+ nsStyleContext *aStyleContext)
+{
+ return ExtractColor(aProperty, aStyleContext).valueOr(NS_RGBA(0, 0, 0, 0));
+}
+
+struct ColorIndexSet {
+ uint8_t colorIndex, alphaIndex;
+};
+
+static const ColorIndexSet gVisitedIndices[2] = { { 0, 0 }, { 1, 0 } };
+
+nscolor
+nsStyleContext::GetVisitedDependentColor(nsCSSPropertyID aProperty)
+{
+ NS_ASSERTION(aProperty == eCSSProperty_color ||
+ aProperty == eCSSProperty_background_color ||
+ aProperty == eCSSProperty_border_top_color ||
+ aProperty == eCSSProperty_border_right_color ||
+ aProperty == eCSSProperty_border_bottom_color ||
+ aProperty == eCSSProperty_border_left_color ||
+ aProperty == eCSSProperty_outline_color ||
+ aProperty == eCSSProperty_column_rule_color ||
+ aProperty == eCSSProperty_text_decoration_color ||
+ aProperty == eCSSProperty_text_emphasis_color ||
+ aProperty == eCSSProperty__webkit_text_fill_color ||
+ aProperty == eCSSProperty__webkit_text_stroke_color ||
+ aProperty == eCSSProperty_fill ||
+ aProperty == eCSSProperty_stroke,
+ "we need to add to nsStyleContext::CalcStyleDifference");
+
+ bool isPaintProperty = aProperty == eCSSProperty_fill ||
+ aProperty == eCSSProperty_stroke;
+
+ nscolor colors[2];
+ colors[0] = isPaintProperty ? ExtractColorLenient(aProperty, this)
+ : ExtractColor(aProperty, this).value();
+
+ nsStyleContext *visitedStyle = this->GetStyleIfVisited();
+ if (!visitedStyle) {
+ return colors[0];
+ }
+
+ colors[1] = isPaintProperty ? ExtractColorLenient(aProperty, visitedStyle)
+ : ExtractColor(aProperty, visitedStyle).value();
+
+ return nsStyleContext::CombineVisitedColors(colors,
+ this->RelevantLinkVisited());
+}
+
+/* static */ nscolor
+nsStyleContext::CombineVisitedColors(nscolor *aColors, bool aLinkIsVisited)
+{
+ if (NS_GET_A(aColors[1]) == 0) {
+ // If the style-if-visited is transparent, then just use the
+ // unvisited style rather than using the (meaningless) color
+ // components of the visited style along with a potentially
+ // non-transparent alpha value.
+ aLinkIsVisited = false;
+ }
+
+ // NOTE: We want this code to have as little timing dependence as
+ // possible on whether this->RelevantLinkVisited() is true.
+ const ColorIndexSet &set =
+ gVisitedIndices[aLinkIsVisited ? 1 : 0];
+
+ nscolor colorColor = aColors[set.colorIndex];
+ nscolor alphaColor = aColors[set.alphaIndex];
+ return NS_RGBA(NS_GET_R(colorColor), NS_GET_G(colorColor),
+ NS_GET_B(colorColor), NS_GET_A(alphaColor));
+}
+
+#ifdef DEBUG
+/* static */ void
+nsStyleContext::AssertStyleStructMaxDifferenceValid()
+{
+#define STYLE_STRUCT(name, checkdata_cb) \
+ MOZ_ASSERT(NS_IsHintSubset(nsStyle##name::DifferenceAlwaysHandledForDescendants(), \
+ nsStyle##name::MaxDifference()));
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT
+}
+
+/* static */ const char*
+nsStyleContext::StructName(nsStyleStructID aSID)
+{
+ switch (aSID) {
+#define STYLE_STRUCT(name_, checkdata_cb) \
+ case eStyleStruct_##name_: \
+ return #name_;
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT
+ default:
+ return "Unknown";
+ }
+}
+
+/* static */ bool
+nsStyleContext::LookupStruct(const nsACString& aName, nsStyleStructID& aResult)
+{
+ if (false)
+ ;
+#define STYLE_STRUCT(name_, checkdata_cb_) \
+ else if (aName.EqualsLiteral(#name_)) \
+ aResult = eStyleStruct_##name_;
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT
+ else
+ return false;
+ return true;
+}
+#endif
+
+void
+nsStyleContext::SwapStyleData(nsStyleContext* aNewContext, uint32_t aStructs)
+{
+ static_assert(nsStyleStructID_Length <= 32, "aStructs is not big enough");
+
+ for (nsStyleStructID i = nsStyleStructID_Inherited_Start;
+ i < nsStyleStructID_Inherited_Start + nsStyleStructID_Inherited_Count;
+ i = nsStyleStructID(i + 1)) {
+ uint32_t bit = nsCachedStyleData::GetBitForSID(i);
+ if (!(aStructs & bit)) {
+ continue;
+ }
+ void*& thisData = mCachedInheritedData.mStyleStructs[i];
+ void*& otherData = aNewContext->mCachedInheritedData.mStyleStructs[i];
+ if (mBits & bit) {
+ if (thisData == otherData) {
+ thisData = nullptr;
+ }
+ } else if (!(aNewContext->mBits & bit) && thisData && otherData) {
+ std::swap(thisData, otherData);
+ }
+ }
+
+ for (nsStyleStructID i = nsStyleStructID_Reset_Start;
+ i < nsStyleStructID_Reset_Start + nsStyleStructID_Reset_Count;
+ i = nsStyleStructID(i + 1)) {
+ uint32_t bit = nsCachedStyleData::GetBitForSID(i);
+ if (!(aStructs & bit)) {
+ continue;
+ }
+ if (!mCachedResetData) {
+ mCachedResetData = new (PresContext()) nsResetStyleData;
+ }
+ if (!aNewContext->mCachedResetData) {
+ aNewContext->mCachedResetData = new (PresContext()) nsResetStyleData;
+ }
+ void*& thisData = mCachedResetData->mStyleStructs[i];
+ void*& otherData = aNewContext->mCachedResetData->mStyleStructs[i];
+ if (mBits & bit) {
+ if (thisData == otherData) {
+ thisData = nullptr;
+ }
+ } else if (!(aNewContext->mBits & bit) && thisData && otherData) {
+ std::swap(thisData, otherData);
+ }
+ }
+}
+
+void
+nsStyleContext::ClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs)
+{
+ if (mChild) {
+ nsStyleContext* child = mChild;
+ do {
+ child->DoClearCachedInheritedStyleDataOnDescendants(aStructs);
+ child = child->mNextSibling;
+ } while (mChild != child);
+ }
+ if (mEmptyChild) {
+ nsStyleContext* child = mEmptyChild;
+ do {
+ child->DoClearCachedInheritedStyleDataOnDescendants(aStructs);
+ child = child->mNextSibling;
+ } while (mEmptyChild != child);
+ }
+}
+
+void
+nsStyleContext::DoClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs)
+{
+ NS_ASSERTION(mFrameRefCnt == 0, "frame still referencing style context");
+ for (nsStyleStructID i = nsStyleStructID_Inherited_Start;
+ i < nsStyleStructID_Inherited_Start + nsStyleStructID_Inherited_Count;
+ i = nsStyleStructID(i + 1)) {
+ uint32_t bit = nsCachedStyleData::GetBitForSID(i);
+ if (aStructs & bit) {
+ if (!(mBits & bit) && mCachedInheritedData.mStyleStructs[i]) {
+ aStructs &= ~bit;
+ } else {
+ mCachedInheritedData.mStyleStructs[i] = nullptr;
+ }
+ }
+ }
+
+ if (mCachedResetData) {
+ for (nsStyleStructID i = nsStyleStructID_Reset_Start;
+ i < nsStyleStructID_Reset_Start + nsStyleStructID_Reset_Count;
+ i = nsStyleStructID(i + 1)) {
+ uint32_t bit = nsCachedStyleData::GetBitForSID(i);
+ if (aStructs & bit) {
+ if (!(mBits & bit) && mCachedResetData->mStyleStructs[i]) {
+ aStructs &= ~bit;
+ } else {
+ mCachedResetData->mStyleStructs[i] = nullptr;
+ }
+ }
+ }
+ }
+
+ if (aStructs == 0) {
+ return;
+ }
+
+ ClearCachedInheritedStyleDataOnDescendants(aStructs);
+}
+
+void
+nsStyleContext::SetIneligibleForSharing()
+{
+ if (mBits & NS_STYLE_INELIGIBLE_FOR_SHARING) {
+ return;
+ }
+ mBits |= NS_STYLE_INELIGIBLE_FOR_SHARING;
+ if (mChild) {
+ nsStyleContext* child = mChild;
+ do {
+ child->SetIneligibleForSharing();
+ child = child->mNextSibling;
+ } while (mChild != child);
+ }
+ if (mEmptyChild) {
+ nsStyleContext* child = mEmptyChild;
+ do {
+ child->SetIneligibleForSharing();
+ child = child->mNextSibling;
+ } while (mEmptyChild != child);
+ }
+}
+
+#ifdef RESTYLE_LOGGING
+nsCString
+nsStyleContext::GetCachedStyleDataAsString(uint32_t aStructs)
+{
+ nsCString structs;
+ for (nsStyleStructID i = nsStyleStructID(0);
+ i < nsStyleStructID_Length;
+ i = nsStyleStructID(i + 1)) {
+ if (aStructs & nsCachedStyleData::GetBitForSID(i)) {
+ const void* data = GetCachedStyleData(i);
+ if (!structs.IsEmpty()) {
+ structs.Append(' ');
+ }
+ structs.AppendPrintf("%s=%p", StructName(i), data);
+ if (HasCachedDependentStyleData(i)) {
+ structs.AppendLiteral("(dependent)");
+ } else {
+ structs.AppendLiteral("(owned)");
+ }
+ }
+ }
+ return structs;
+}
+
+int32_t&
+nsStyleContext::LoggingDepth()
+{
+ static int32_t depth = 0;
+ return depth;
+}
+
+void
+nsStyleContext::LogStyleContextTree(int32_t aLoggingDepth, uint32_t aStructs)
+{
+ LoggingDepth() = aLoggingDepth;
+ LogStyleContextTree(true, aStructs);
+}
+
+void
+nsStyleContext::LogStyleContextTree(bool aFirst, uint32_t aStructs)
+{
+ nsCString structs = GetCachedStyleDataAsString(aStructs);
+ if (!structs.IsEmpty()) {
+ structs.Append(' ');
+ }
+
+ nsCString pseudo;
+ if (mPseudoTag) {
+ nsAutoString pseudoTag;
+ mPseudoTag->ToString(pseudoTag);
+ AppendUTF16toUTF8(pseudoTag, pseudo);
+ pseudo.Append(' ');
+ }
+
+ nsCString flags;
+ if (IsStyleIfVisited()) {
+ flags.AppendLiteral("IS_STYLE_IF_VISITED ");
+ }
+ if (HasChildThatUsesGrandancestorStyle()) {
+ flags.AppendLiteral("CHILD_USES_GRANDANCESTOR_STYLE ");
+ }
+ if (IsShared()) {
+ flags.AppendLiteral("IS_SHARED ");
+ }
+
+ nsCString parent;
+ if (aFirst) {
+ parent.AppendPrintf("parent=%p ", mParent.get());
+ }
+
+ LOG_RESTYLE("%p(%d) %s%s%s%s",
+ this, mRefCnt,
+ structs.get(), pseudo.get(), flags.get(), parent.get());
+
+ LOG_RESTYLE_INDENT();
+
+ if (nullptr != mChild) {
+ nsStyleContext* child = mChild;
+ do {
+ child->LogStyleContextTree(false, aStructs);
+ child = child->mNextSibling;
+ } while (mChild != child);
+ }
+ if (nullptr != mEmptyChild) {
+ nsStyleContext* child = mEmptyChild;
+ do {
+ child->LogStyleContextTree(false, aStructs);
+ child = child->mNextSibling;
+ } while (mEmptyChild != child);
+ }
+}
+#endif
+
+#ifdef DEBUG
+/* static */ void
+nsStyleContext::Initialize()
+{
+ Preferences::AddBoolVarCache(
+ &sExpensiveStyleStructAssertionsEnabled,
+ "layout.css.expensive-style-struct-assertions.enabled");
+}
+#endif