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: #ifndef _nsStyleContext_h_ michael@0: #define _nsStyleContext_h_ michael@0: michael@0: #include "nsRuleNode.h" michael@0: #include "nsCSSPseudoElements.h" michael@0: michael@0: class nsIAtom; michael@0: class nsPresContext; michael@0: michael@0: /** michael@0: * An nsStyleContext represents the computed style data for an element. michael@0: * The computed style data are stored in a set of structs (see michael@0: * nsStyleStruct.h) that are cached either on the style context or in michael@0: * the rule tree (see nsRuleNode.h for a description of this caching and michael@0: * how the cached structs are shared). michael@0: * michael@0: * Since the data in |nsIStyleRule|s and |nsRuleNode|s are immutable michael@0: * (with a few exceptions, like system color changes), the data in an michael@0: * nsStyleContext are also immutable (with the additional exception of michael@0: * GetUniqueStyleData). When style data change, michael@0: * nsFrameManager::ReResolveStyleContext creates a new style context. michael@0: * michael@0: * Style contexts are reference counted. References are generally held michael@0: * by: michael@0: * 1. the |nsIFrame|s that are using the style context and michael@0: * 2. any *child* style contexts (this might be the reverse of michael@0: * expectation, but it makes sense in this case) michael@0: * Style contexts participate in the mark phase of rule node garbage michael@0: * collection. michael@0: */ michael@0: michael@0: class nsStyleContext michael@0: { michael@0: public: michael@0: /** michael@0: * Create a new style context. michael@0: * @param aParent The parent of a style context is used for CSS michael@0: * inheritance. When the element or pseudo-element michael@0: * this style context represents the style data of michael@0: * inherits a CSS property, the value comes from the michael@0: * parent style context. This means style context michael@0: * parentage must match the definitions of inheritance michael@0: * in the CSS specification. michael@0: * @param aPseudoTag The pseudo-element or anonymous box for which michael@0: * this style context represents style. Null if michael@0: * this style context is for a normal DOM element. michael@0: * @param aPseudoType Must match aPseudoTag. michael@0: * @param aRuleNode A rule node representing the ordered sequence of michael@0: * rules that any element, pseudo-element, or michael@0: * anonymous box that this style context is for michael@0: * matches. See |nsRuleNode| and |nsIStyleRule|. michael@0: * @param aSkipFlexItemStyleFixup michael@0: * If set, this flag indicates that we should skip michael@0: * the chunk of ApplyStyleFixups() that modifies flex michael@0: * items' display values. michael@0: */ michael@0: nsStyleContext(nsStyleContext* aParent, nsIAtom* aPseudoTag, michael@0: nsCSSPseudoElements::Type aPseudoType, michael@0: nsRuleNode* aRuleNode, michael@0: bool aSkipFlexItemStyleFixup); michael@0: ~nsStyleContext(); michael@0: michael@0: void* operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW; michael@0: void Destroy(); michael@0: michael@0: nsrefcnt AddRef() { michael@0: if (mRefCnt == UINT32_MAX) { michael@0: NS_WARNING("refcount overflow, leaking object"); michael@0: return mRefCnt; michael@0: } michael@0: ++mRefCnt; michael@0: NS_LOG_ADDREF(this, mRefCnt, "nsStyleContext", sizeof(nsStyleContext)); michael@0: return mRefCnt; michael@0: } michael@0: michael@0: nsrefcnt Release() { michael@0: if (mRefCnt == UINT32_MAX) { michael@0: NS_WARNING("refcount overflow, leaking object"); michael@0: return mRefCnt; michael@0: } michael@0: --mRefCnt; michael@0: NS_LOG_RELEASE(this, mRefCnt, "nsStyleContext"); michael@0: if (mRefCnt == 0) { michael@0: Destroy(); michael@0: return 0; michael@0: } michael@0: return mRefCnt; michael@0: } michael@0: michael@0: nsPresContext* PresContext() const { return mRuleNode->PresContext(); } michael@0: michael@0: nsStyleContext* GetParent() const { return mParent; } michael@0: michael@0: nsIAtom* GetPseudo() const { return mPseudoTag; } michael@0: nsCSSPseudoElements::Type GetPseudoType() const { michael@0: return static_cast(mBits >> michael@0: NS_STYLE_CONTEXT_TYPE_SHIFT); michael@0: } michael@0: michael@0: // Find, if it already exists *and is easily findable* (i.e., near the michael@0: // start of the child list), a style context whose: michael@0: // * GetPseudo() matches aPseudoTag michael@0: // * RuleNode() matches aRules michael@0: // * !GetStyleIfVisited() == !aRulesIfVisited, and, if they're michael@0: // non-null, GetStyleIfVisited()->RuleNode() == aRulesIfVisited michael@0: // * RelevantLinkVisited() == aRelevantLinkVisited michael@0: already_AddRefed michael@0: FindChildWithRules(const nsIAtom* aPseudoTag, nsRuleNode* aRules, michael@0: nsRuleNode* aRulesIfVisited, michael@0: bool aRelevantLinkVisited); michael@0: michael@0: // Does this style context or any of its ancestors have text michael@0: // decoration lines? michael@0: bool HasTextDecorationLines() const michael@0: { return !!(mBits & NS_STYLE_HAS_TEXT_DECORATION_LINES); } michael@0: michael@0: // Does this style context represent the style for a pseudo-element or michael@0: // inherit data from such a style context? Whether this returns true michael@0: // is equivalent to whether it or any of its ancestors returns michael@0: // non-null for GetPseudo. michael@0: bool HasPseudoElementData() const michael@0: { return !!(mBits & NS_STYLE_HAS_PSEUDO_ELEMENT_DATA); } michael@0: michael@0: // Is the only link whose visitedness is allowed to influence the michael@0: // style of the node this style context is for (which is that element michael@0: // or its nearest ancestor that is a link) visited? michael@0: bool RelevantLinkVisited() const michael@0: { return !!(mBits & NS_STYLE_RELEVANT_LINK_VISITED); } michael@0: michael@0: // Is this a style context for a link? michael@0: bool IsLinkContext() const { michael@0: return michael@0: GetStyleIfVisited() && GetStyleIfVisited()->GetParent() == GetParent(); michael@0: } michael@0: michael@0: // Is this style context the GetStyleIfVisited() for some other style michael@0: // context? michael@0: bool IsStyleIfVisited() const michael@0: { return !!(mBits & NS_STYLE_IS_STYLE_IF_VISITED); } michael@0: michael@0: // Tells this style context that it should return true from michael@0: // IsStyleIfVisited. michael@0: void SetIsStyleIfVisited() michael@0: { mBits |= NS_STYLE_IS_STYLE_IF_VISITED; } michael@0: michael@0: // Return the style context whose style data should be used for the R, michael@0: // G, and B components of color, background-color, and border-*-color michael@0: // if RelevantLinkIsVisited(). michael@0: // michael@0: // GetPseudo() and GetPseudoType() on this style context return the michael@0: // same as on |this|, and its depth in the tree (number of GetParent() michael@0: // calls until null is returned) is the same as |this|, since its michael@0: // parent is either |this|'s parent or |this|'s parent's michael@0: // style-if-visited. michael@0: // michael@0: // Structs on this context should never be examined without also michael@0: // examining the corresponding struct on |this|. Doing so will likely michael@0: // both (1) lead to a privacy leak and (2) lead to dynamic change bugs michael@0: // related to the Peek code in nsStyleContext::CalcStyleDifference. michael@0: nsStyleContext* GetStyleIfVisited() const michael@0: { return mStyleIfVisited; } michael@0: michael@0: // To be called only from nsStyleSet. michael@0: void SetStyleIfVisited(already_AddRefed aStyleIfVisited) michael@0: { michael@0: NS_ABORT_IF_FALSE(!IsStyleIfVisited(), "this context is not visited data"); michael@0: NS_ASSERTION(!mStyleIfVisited, "should only be set once"); michael@0: michael@0: mStyleIfVisited = aStyleIfVisited; michael@0: michael@0: NS_ABORT_IF_FALSE(mStyleIfVisited->IsStyleIfVisited(), michael@0: "other context is visited data"); michael@0: NS_ABORT_IF_FALSE(!mStyleIfVisited->GetStyleIfVisited(), michael@0: "other context does not have visited data"); michael@0: NS_ASSERTION(GetStyleIfVisited()->GetPseudo() == GetPseudo(), michael@0: "pseudo tag mismatch"); michael@0: if (GetParent() && GetParent()->GetStyleIfVisited()) { michael@0: NS_ASSERTION(GetStyleIfVisited()->GetParent() == michael@0: GetParent()->GetStyleIfVisited() || michael@0: GetStyleIfVisited()->GetParent() == GetParent(), michael@0: "parent mismatch"); michael@0: } else { michael@0: NS_ASSERTION(GetStyleIfVisited()->GetParent() == GetParent(), michael@0: "parent mismatch"); michael@0: } michael@0: } michael@0: michael@0: // Tell this style context to cache aStruct as the struct for aSID michael@0: void SetStyle(nsStyleStructID aSID, void* aStruct); michael@0: michael@0: // Setters for inherit structs only, since rulenode only sets those eagerly. michael@0: #define STYLE_STRUCT_INHERITED(name_, checkdata_cb_) \ michael@0: void SetStyle##name_ (nsStyle##name_ * aStruct) { \ michael@0: void *& slot = \ michael@0: mCachedInheritedData.mStyleStructs[eStyleStruct_##name_]; \ michael@0: NS_ASSERTION(!slot || \ michael@0: (mBits & \ michael@0: nsCachedStyleData::GetBitForSID(eStyleStruct_##name_)), \ michael@0: "Going to leak styledata"); \ michael@0: slot = aStruct; \ michael@0: } michael@0: #define STYLE_STRUCT_RESET(name_, checkdata_cb_) /* nothing */ michael@0: #include "nsStyleStructList.h" michael@0: #undef STYLE_STRUCT_RESET michael@0: #undef STYLE_STRUCT_INHERITED michael@0: michael@0: nsRuleNode* RuleNode() { return mRuleNode; } michael@0: void AddStyleBit(const uint64_t& aBit) { mBits |= aBit; } michael@0: michael@0: /* michael@0: * Mark this style context's rule node (and its ancestors) to prevent michael@0: * it from being garbage collected. michael@0: */ michael@0: void Mark(); michael@0: michael@0: /* michael@0: * Get the style data for a style struct. This is the most important michael@0: * member function of nsIStyleContext. It fills in a const pointer michael@0: * to a style data struct that is appropriate for the style context's michael@0: * frame. This struct may be shared with other contexts (either in michael@0: * the rule tree or the style context tree), so it should not be michael@0: * modified. michael@0: * michael@0: * This function will NOT return null (even when out of memory) when michael@0: * given a valid style struct ID, so the result does not need to be michael@0: * null-checked. michael@0: * michael@0: * The typesafe functions below are preferred to the use of this michael@0: * function, both because they're easier to read and because they're michael@0: * faster. michael@0: */ michael@0: const void* NS_FASTCALL StyleData(nsStyleStructID aSID); michael@0: michael@0: /** michael@0: * Define typesafe getter functions for each style struct by michael@0: * preprocessing the list of style structs. These functions are the michael@0: * preferred way to get style data. The macro creates functions like: michael@0: * const nsStyleBorder* StyleBorder(); michael@0: * const nsStyleColor* StyleColor(); michael@0: */ michael@0: #define STYLE_STRUCT(name_, checkdata_cb_) \ michael@0: const nsStyle##name_ * Style##name_() { \ michael@0: return DoGetStyle##name_(true); \ michael@0: } michael@0: #include "nsStyleStructList.h" michael@0: #undef STYLE_STRUCT michael@0: michael@0: /** michael@0: * PeekStyle* is like GetStyle* but doesn't trigger style michael@0: * computation if the data is not cached on either the style context michael@0: * or the rule node. michael@0: * michael@0: * Perhaps this shouldn't be a public nsStyleContext API. michael@0: */ michael@0: #define STYLE_STRUCT(name_, checkdata_cb_) \ michael@0: const nsStyle##name_ * PeekStyle##name_() { \ michael@0: return DoGetStyle##name_(false); \ michael@0: } michael@0: #include "nsStyleStructList.h" michael@0: #undef STYLE_STRUCT michael@0: michael@0: void* GetUniqueStyleData(const nsStyleStructID& aSID); michael@0: michael@0: /** michael@0: * Compute the style changes needed during restyling when this style michael@0: * context is being replaced by aOther. (This is nonsymmetric since michael@0: * we optimize by skipping comparison for styles that have never been michael@0: * requested.) michael@0: * michael@0: * This method returns a change hint (see nsChangeHint.h). All change michael@0: * hints apply to the frame and its later continuations or ib-split michael@0: * siblings. Most (all of those except the "NotHandledForDescendants" michael@0: * hints) also apply to all descendants. The caller must pass in any michael@0: * non-inherited hints that resulted from the parent style context's michael@0: * style change. The caller *may* pass more hints than needed, but michael@0: * must not pass less than needed; therefore if the caller doesn't michael@0: * know, the caller should pass michael@0: * nsChangeHint_Hints_NotHandledForDescendants. michael@0: */ michael@0: nsChangeHint CalcStyleDifference(nsStyleContext* aOther, michael@0: nsChangeHint aParentHintsNotHandledForDescendants); michael@0: michael@0: /** michael@0: * Get a color that depends on link-visitedness using this and michael@0: * this->GetStyleIfVisited(). michael@0: * michael@0: * aProperty must be a color-valued property that nsStyleAnimation michael@0: * knows how to extract. It must also be a property that we know to michael@0: * do change handling for in nsStyleContext::CalcDifference. michael@0: * michael@0: * Note that if aProperty is eCSSProperty_border_*_color, this michael@0: * function handles -moz-use-text-color. michael@0: */ michael@0: nscolor GetVisitedDependentColor(nsCSSProperty aProperty); michael@0: michael@0: /** michael@0: * aColors should be a two element array of nscolor in which the first michael@0: * color is the unvisited color and the second is the visited color. michael@0: * michael@0: * Combine the R, G, and B components of whichever of aColors should michael@0: * be used based on aLinkIsVisited with the A component of aColors[0]. michael@0: */ michael@0: static nscolor CombineVisitedColors(nscolor *aColors, michael@0: bool aLinkIsVisited); michael@0: michael@0: /** michael@0: * Allocate a chunk of memory that is scoped to the lifetime of this michael@0: * style context, i.e., memory that will automatically be freed when michael@0: * this style context is destroyed. This is intended for allocations michael@0: * that are stored on this style context or its style structs. (Use michael@0: * on style structs is fine since any style context to which this michael@0: * context's style structs are shared will be a descendant of this michael@0: * style context and thus keep it alive.) michael@0: * michael@0: * This currently allocates the memory out of the pres shell arena. michael@0: * michael@0: * It would be relatively straightforward to write a Free method michael@0: * for the underlying implementation, but we don't need it (or the michael@0: * overhead of making a doubly-linked list or other structure to michael@0: * support it). michael@0: * michael@0: * WARNING: Memory allocated using this method cannot be stored in the michael@0: * rule tree, since rule nodes may outlive the style context. michael@0: */ michael@0: void* Alloc(size_t aSize); michael@0: michael@0: /** michael@0: * Start the background image loads for this style context. michael@0: */ michael@0: void StartBackgroundImageLoads() { michael@0: // Just get our background struct; that should do the trick michael@0: StyleBackground(); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void List(FILE* out, int32_t aIndent); michael@0: static void AssertStyleStructMaxDifferenceValid(); michael@0: #endif michael@0: michael@0: protected: michael@0: void AddChild(nsStyleContext* aChild); michael@0: void RemoveChild(nsStyleContext* aChild); michael@0: michael@0: void ApplyStyleFixups(bool aSkipFlexItemStyleFixup); michael@0: michael@0: void FreeAllocations(nsPresContext* aPresContext); michael@0: michael@0: // Helper function that GetStyleData and GetUniqueStyleData use. Only michael@0: // returns the structs we cache ourselves; never consults the ruletree. michael@0: inline const void* GetCachedStyleData(nsStyleStructID aSID); michael@0: michael@0: // Helper functions for GetStyle* and PeekStyle* michael@0: #define STYLE_STRUCT_INHERITED(name_, checkdata_cb_) \ michael@0: const nsStyle##name_ * DoGetStyle##name_(bool aComputeData) { \ michael@0: const nsStyle##name_ * cachedData = \ michael@0: static_cast( \ michael@0: mCachedInheritedData.mStyleStructs[eStyleStruct_##name_]); \ michael@0: if (cachedData) /* Have it cached already, yay */ \ michael@0: return cachedData; \ michael@0: /* Have the rulenode deal */ \ michael@0: return mRuleNode->GetStyle##name_(this, aComputeData); \ michael@0: } michael@0: #define STYLE_STRUCT_RESET(name_, checkdata_cb_) \ michael@0: const nsStyle##name_ * DoGetStyle##name_(bool aComputeData) { \ michael@0: const nsStyle##name_ * cachedData = mCachedResetData \ michael@0: ? static_cast( \ michael@0: mCachedResetData->mStyleStructs[eStyleStruct_##name_]) \ michael@0: : nullptr; \ michael@0: if (cachedData) /* Have it cached already, yay */ \ michael@0: return cachedData; \ michael@0: /* Have the rulenode deal */ \ michael@0: return mRuleNode->GetStyle##name_(this, aComputeData); \ michael@0: } michael@0: #include "nsStyleStructList.h" michael@0: #undef STYLE_STRUCT_RESET michael@0: #undef STYLE_STRUCT_INHERITED michael@0: michael@0: nsStyleContext* const mParent; // STRONG michael@0: michael@0: // Children are kept in two circularly-linked lists. The list anchor michael@0: // is not part of the list (null for empty), and we point to the first michael@0: // child. michael@0: // mEmptyChild for children whose rule node is the root rule node, and michael@0: // mChild for other children. The order of children is not michael@0: // meaningful. michael@0: nsStyleContext* mChild; michael@0: nsStyleContext* mEmptyChild; michael@0: nsStyleContext* mPrevSibling; michael@0: nsStyleContext* mNextSibling; michael@0: michael@0: // Style to be used instead for the R, G, and B components of color, michael@0: // background-color, and border-*-color if the nearest ancestor link michael@0: // element is visited (see RelevantLinkVisited()). michael@0: nsRefPtr mStyleIfVisited; michael@0: michael@0: // If this style context is for a pseudo-element or anonymous box, michael@0: // the relevant atom. michael@0: nsCOMPtr mPseudoTag; michael@0: michael@0: // The rule node is the node in the lexicographic tree of rule nodes michael@0: // (the "rule tree") that indicates which style rules are used to michael@0: // compute the style data, and in what cascading order. The least michael@0: // specific rule matched is the one whose rule node is a child of the michael@0: // root of the rule tree, and the most specific rule matched is the michael@0: // |mRule| member of |mRuleNode|. michael@0: nsRuleNode* const mRuleNode; michael@0: michael@0: // Private to nsStyleContext::Alloc and FreeAllocations. michael@0: struct AllocationHeader { michael@0: AllocationHeader* mNext; michael@0: size_t mSize; michael@0: michael@0: void* mStorageStart; // ensure the storage is at least pointer-aligned michael@0: }; michael@0: AllocationHeader* mAllocations; michael@0: michael@0: // mCachedInheritedData and mCachedResetData point to both structs that michael@0: // are owned by this style context and structs that are owned by one of michael@0: // this style context's ancestors (which are indirectly owned since this michael@0: // style context owns a reference to its parent). If the bit in |mBits| michael@0: // is set for a struct, that means that the pointer for that struct is michael@0: // owned by an ancestor or by mRuleNode rather than by this style context. michael@0: // Since style contexts typically have some inherited data but only sometimes michael@0: // have reset data, we always allocate the mCachedInheritedData, but only michael@0: // sometimes allocate the mCachedResetData. michael@0: nsResetStyleData* mCachedResetData; // Cached reset style data. michael@0: nsInheritedStyleData mCachedInheritedData; // Cached inherited style data michael@0: uint64_t mBits; // Which structs are inherited from the michael@0: // parent context or owned by mRuleNode. michael@0: uint32_t mRefCnt; 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: #endif