1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/base/nsCounterManager.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,266 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +// vim:cindent:ai:sw=4:ts=4:et: 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* implementation of CSS counters (for numbering things) */ 1.11 + 1.12 +#ifndef nsCounterManager_h_ 1.13 +#define nsCounterManager_h_ 1.14 + 1.15 +#include "mozilla/Attributes.h" 1.16 +#include "nsGenConList.h" 1.17 +#include "nsAutoPtr.h" 1.18 +#include "nsClassHashtable.h" 1.19 +#include "mozilla/Likely.h" 1.20 + 1.21 +class nsCounterList; 1.22 +struct nsCounterUseNode; 1.23 +struct nsCounterChangeNode; 1.24 + 1.25 +struct nsCounterNode : public nsGenConNode { 1.26 + enum Type { 1.27 + RESET, // a "counter number" pair in 'counter-reset' 1.28 + INCREMENT, // a "counter number" pair in 'counter-increment' 1.29 + USE // counter() or counters() in 'content' 1.30 + }; 1.31 + 1.32 + Type mType; 1.33 + 1.34 + // Counter value after this node 1.35 + int32_t mValueAfter; 1.36 + 1.37 + // mScopeStart points to the node (usually a RESET, but not in the 1.38 + // case of an implied 'counter-reset') that created the scope for 1.39 + // this element (for a RESET, its outer scope, i.e., the one it is 1.40 + // inside rather than the one it creates). 1.41 + 1.42 + // May be null for all types, but only when mScopePrev is also null. 1.43 + // Being null for a non-RESET means that it is an implied 1.44 + // 'counter-reset'. Being null for a RESET means it has no outer 1.45 + // scope. 1.46 + nsCounterNode *mScopeStart; 1.47 + 1.48 + // mScopePrev points to the previous node that is in the same scope, 1.49 + // or for a RESET, the previous node in the scope outside of the 1.50 + // reset. 1.51 + 1.52 + // May be null for all types, but only when mScopeStart is also 1.53 + // null. Following the mScopePrev links will eventually lead to 1.54 + // mScopeStart. Being null for a non-RESET means that it is an 1.55 + // implied 'counter-reset'. Being null for a RESET means it has no 1.56 + // outer scope. 1.57 + nsCounterNode *mScopePrev; 1.58 + 1.59 + inline nsCounterUseNode* UseNode(); 1.60 + inline nsCounterChangeNode* ChangeNode(); 1.61 + 1.62 + // For RESET and INCREMENT nodes, aPseudoFrame need not be a 1.63 + // pseudo-element, and aContentIndex represents the index within the 1.64 + // 'counter-reset' or 'counter-increment' property instead of within 1.65 + // the 'content' property but offset to ensure that (reset, 1.66 + // increment, use) sort in that order. (This slight weirdness 1.67 + // allows sharing a lot of code with 'quotes'.) 1.68 + nsCounterNode(int32_t aContentIndex, Type aType) 1.69 + : nsGenConNode(aContentIndex) 1.70 + , mType(aType) 1.71 + , mValueAfter(0) 1.72 + , mScopeStart(nullptr) 1.73 + , mScopePrev(nullptr) 1.74 + { 1.75 + } 1.76 + 1.77 + // to avoid virtual function calls in the common case 1.78 + inline void Calc(nsCounterList* aList); 1.79 +}; 1.80 + 1.81 +struct nsCounterUseNode : public nsCounterNode { 1.82 + // The same structure passed through the style system: an array 1.83 + // containing the values in the counter() or counters() in the order 1.84 + // given in the CSS spec. 1.85 + nsRefPtr<nsCSSValue::Array> mCounterStyle; 1.86 + 1.87 + // false for counter(), true for counters() 1.88 + bool mAllCounters; 1.89 + 1.90 + // args go directly to member variables here and of nsGenConNode 1.91 + nsCounterUseNode(nsCSSValue::Array* aCounterStyle, 1.92 + uint32_t aContentIndex, bool aAllCounters) 1.93 + : nsCounterNode(aContentIndex, USE) 1.94 + , mCounterStyle(aCounterStyle) 1.95 + , mAllCounters(aAllCounters) 1.96 + { 1.97 + NS_ASSERTION(aContentIndex <= INT32_MAX, "out of range"); 1.98 + } 1.99 + 1.100 + virtual bool InitTextFrame(nsGenConList* aList, 1.101 + nsIFrame* aPseudoFrame, nsIFrame* aTextFrame) MOZ_OVERRIDE; 1.102 + 1.103 + // assign the correct |mValueAfter| value to a node that has been inserted 1.104 + // Should be called immediately after calling |Insert|. 1.105 + void Calc(nsCounterList* aList); 1.106 + 1.107 + // The text that should be displayed for this counter. 1.108 + void GetText(nsString& aResult); 1.109 +}; 1.110 + 1.111 +struct nsCounterChangeNode : public nsCounterNode { 1.112 + int32_t mChangeValue; // the numeric value of the increment or reset 1.113 + 1.114 + // |aPseudoFrame| is not necessarily a pseudo-element's frame, but 1.115 + // since it is for every other subclass of nsGenConNode, we follow 1.116 + // the naming convention here. 1.117 + // |aPropIndex| is the index of the value within the list in the 1.118 + // 'counter-increment' or 'counter-reset' property. 1.119 + nsCounterChangeNode(nsIFrame* aPseudoFrame, 1.120 + nsCounterNode::Type aChangeType, 1.121 + int32_t aChangeValue, 1.122 + int32_t aPropIndex) 1.123 + : nsCounterNode(// Fake a content index for resets and increments 1.124 + // that comes before all the real content, with 1.125 + // the resets first, in order, and then the increments. 1.126 + aPropIndex + (aChangeType == RESET 1.127 + ? (INT32_MIN) 1.128 + : (INT32_MIN / 2)), 1.129 + aChangeType) 1.130 + , mChangeValue(aChangeValue) 1.131 + { 1.132 + NS_ASSERTION(aPropIndex >= 0, "out of range"); 1.133 + NS_ASSERTION(aChangeType == INCREMENT || aChangeType == RESET, 1.134 + "bad type"); 1.135 + mPseudoFrame = aPseudoFrame; 1.136 + CheckFrameAssertions(); 1.137 + } 1.138 + 1.139 + // assign the correct |mValueAfter| value to a node that has been inserted 1.140 + // Should be called immediately after calling |Insert|. 1.141 + void Calc(nsCounterList* aList); 1.142 +}; 1.143 + 1.144 +inline nsCounterUseNode* nsCounterNode::UseNode() 1.145 +{ 1.146 + NS_ASSERTION(mType == USE, "wrong type"); 1.147 + return static_cast<nsCounterUseNode*>(this); 1.148 +} 1.149 + 1.150 +inline nsCounterChangeNode* nsCounterNode::ChangeNode() 1.151 +{ 1.152 + NS_ASSERTION(mType == INCREMENT || mType == RESET, "wrong type"); 1.153 + return static_cast<nsCounterChangeNode*>(this); 1.154 +} 1.155 + 1.156 +inline void nsCounterNode::Calc(nsCounterList* aList) 1.157 +{ 1.158 + if (mType == USE) 1.159 + UseNode()->Calc(aList); 1.160 + else 1.161 + ChangeNode()->Calc(aList); 1.162 +} 1.163 + 1.164 +class nsCounterList : public nsGenConList { 1.165 +public: 1.166 + nsCounterList() : nsGenConList(), 1.167 + mDirty(false) 1.168 + {} 1.169 + 1.170 + void Insert(nsCounterNode* aNode) { 1.171 + nsGenConList::Insert(aNode); 1.172 + // Don't SetScope if we're dirty -- we'll reset all the scopes anyway, 1.173 + // and we can't usefully compute scopes right now. 1.174 + if (MOZ_LIKELY(!IsDirty())) { 1.175 + SetScope(aNode); 1.176 + } 1.177 + } 1.178 + 1.179 + nsCounterNode* First() { 1.180 + return static_cast<nsCounterNode*>(mFirstNode); 1.181 + } 1.182 + 1.183 + static nsCounterNode* Next(nsCounterNode* aNode) { 1.184 + return static_cast<nsCounterNode*>(nsGenConList::Next(aNode)); 1.185 + } 1.186 + static nsCounterNode* Prev(nsCounterNode* aNode) { 1.187 + return static_cast<nsCounterNode*>(nsGenConList::Prev(aNode)); 1.188 + } 1.189 + 1.190 + static int32_t ValueBefore(nsCounterNode* aNode) { 1.191 + return aNode->mScopePrev ? aNode->mScopePrev->mValueAfter : 0; 1.192 + } 1.193 + 1.194 + // Correctly set |aNode->mScopeStart| and |aNode->mScopePrev| 1.195 + void SetScope(nsCounterNode *aNode); 1.196 + 1.197 + // Recalculate |mScopeStart|, |mScopePrev|, and |mValueAfter| for 1.198 + // all nodes and update text in text content nodes. 1.199 + void RecalcAll(); 1.200 + 1.201 + bool IsDirty() { return mDirty; } 1.202 + void SetDirty() { mDirty = true; } 1.203 + 1.204 +private: 1.205 + bool mDirty; 1.206 +}; 1.207 + 1.208 +/** 1.209 + * The counter manager maintains an |nsCounterList| for each named 1.210 + * counter to keep track of all scopes with that name. 1.211 + */ 1.212 +class nsCounterManager { 1.213 +public: 1.214 + nsCounterManager(); 1.215 + // Returns true if dirty 1.216 + bool AddCounterResetsAndIncrements(nsIFrame *aFrame); 1.217 + 1.218 + // Gets the appropriate counter list, creating it if necessary. 1.219 + // Returns null only on out-of-memory. 1.220 + nsCounterList* CounterListFor(const nsSubstring& aCounterName); 1.221 + 1.222 + // Clean up data in any dirty counter lists. 1.223 + void RecalcAll(); 1.224 + 1.225 + // Destroy nodes for the frame in any lists, and return whether any 1.226 + // nodes were destroyed. 1.227 + bool DestroyNodesFor(nsIFrame *aFrame); 1.228 + 1.229 + // Clear all data. 1.230 + void Clear() { mNames.Clear(); } 1.231 + 1.232 +#ifdef DEBUG 1.233 + void Dump(); 1.234 +#endif 1.235 + 1.236 + static int32_t IncrementCounter(int32_t aOldValue, int32_t aIncrement) 1.237 + { 1.238 + // Addition of unsigned values is defined to be arithmetic 1.239 + // modulo 2^bits (C++ 2011, 3.9.1 [basic.fundamental], clause 4); 1.240 + // addition of signed values is undefined (and clang does 1.241 + // something very strange if we use it here). Likewise integral 1.242 + // conversion from signed to unsigned is also defined as modulo 1.243 + // 2^bits (C++ 2011, 4.7 [conv.integral], clause 2); conversion 1.244 + // from unsigned to signed is however undefined (ibid., clause 3), 1.245 + // but to do what we want we must nonetheless depend on that 1.246 + // small piece of undefined behavior. 1.247 + int32_t newValue = int32_t(uint32_t(aOldValue) + uint32_t(aIncrement)); 1.248 + // The CSS Working Group resolved that a counter-increment that 1.249 + // exceeds internal limits should not increment at all. 1.250 + // http://lists.w3.org/Archives/Public/www-style/2013Feb/0392.html 1.251 + // (This means, for example, that if aIncrement is 5, the 1.252 + // counter will get stuck at the largest multiple of 5 less than 1.253 + // the maximum 32-bit integer.) 1.254 + if ((aIncrement > 0) != (newValue > aOldValue)) { 1.255 + newValue = aOldValue; 1.256 + } 1.257 + return newValue; 1.258 + } 1.259 + 1.260 +private: 1.261 + // for |AddCounterResetsAndIncrements| only 1.262 + bool AddResetOrIncrement(nsIFrame *aFrame, int32_t aIndex, 1.263 + const nsStyleCounterData *aCounterData, 1.264 + nsCounterNode::Type aType); 1.265 + 1.266 + nsClassHashtable<nsStringHashKey, nsCounterList> mNames; 1.267 +}; 1.268 + 1.269 +#endif /* nsCounterManager_h_ */