layout/base/nsCounterManager.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 // vim:cindent:ai:sw=4:ts=4:et:
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /* implementation of CSS counters (for numbering things) */
michael@0 8
michael@0 9 #ifndef nsCounterManager_h_
michael@0 10 #define nsCounterManager_h_
michael@0 11
michael@0 12 #include "mozilla/Attributes.h"
michael@0 13 #include "nsGenConList.h"
michael@0 14 #include "nsAutoPtr.h"
michael@0 15 #include "nsClassHashtable.h"
michael@0 16 #include "mozilla/Likely.h"
michael@0 17
michael@0 18 class nsCounterList;
michael@0 19 struct nsCounterUseNode;
michael@0 20 struct nsCounterChangeNode;
michael@0 21
michael@0 22 struct nsCounterNode : public nsGenConNode {
michael@0 23 enum Type {
michael@0 24 RESET, // a "counter number" pair in 'counter-reset'
michael@0 25 INCREMENT, // a "counter number" pair in 'counter-increment'
michael@0 26 USE // counter() or counters() in 'content'
michael@0 27 };
michael@0 28
michael@0 29 Type mType;
michael@0 30
michael@0 31 // Counter value after this node
michael@0 32 int32_t mValueAfter;
michael@0 33
michael@0 34 // mScopeStart points to the node (usually a RESET, but not in the
michael@0 35 // case of an implied 'counter-reset') that created the scope for
michael@0 36 // this element (for a RESET, its outer scope, i.e., the one it is
michael@0 37 // inside rather than the one it creates).
michael@0 38
michael@0 39 // May be null for all types, but only when mScopePrev is also null.
michael@0 40 // Being null for a non-RESET means that it is an implied
michael@0 41 // 'counter-reset'. Being null for a RESET means it has no outer
michael@0 42 // scope.
michael@0 43 nsCounterNode *mScopeStart;
michael@0 44
michael@0 45 // mScopePrev points to the previous node that is in the same scope,
michael@0 46 // or for a RESET, the previous node in the scope outside of the
michael@0 47 // reset.
michael@0 48
michael@0 49 // May be null for all types, but only when mScopeStart is also
michael@0 50 // null. Following the mScopePrev links will eventually lead to
michael@0 51 // mScopeStart. Being null for a non-RESET means that it is an
michael@0 52 // implied 'counter-reset'. Being null for a RESET means it has no
michael@0 53 // outer scope.
michael@0 54 nsCounterNode *mScopePrev;
michael@0 55
michael@0 56 inline nsCounterUseNode* UseNode();
michael@0 57 inline nsCounterChangeNode* ChangeNode();
michael@0 58
michael@0 59 // For RESET and INCREMENT nodes, aPseudoFrame need not be a
michael@0 60 // pseudo-element, and aContentIndex represents the index within the
michael@0 61 // 'counter-reset' or 'counter-increment' property instead of within
michael@0 62 // the 'content' property but offset to ensure that (reset,
michael@0 63 // increment, use) sort in that order. (This slight weirdness
michael@0 64 // allows sharing a lot of code with 'quotes'.)
michael@0 65 nsCounterNode(int32_t aContentIndex, Type aType)
michael@0 66 : nsGenConNode(aContentIndex)
michael@0 67 , mType(aType)
michael@0 68 , mValueAfter(0)
michael@0 69 , mScopeStart(nullptr)
michael@0 70 , mScopePrev(nullptr)
michael@0 71 {
michael@0 72 }
michael@0 73
michael@0 74 // to avoid virtual function calls in the common case
michael@0 75 inline void Calc(nsCounterList* aList);
michael@0 76 };
michael@0 77
michael@0 78 struct nsCounterUseNode : public nsCounterNode {
michael@0 79 // The same structure passed through the style system: an array
michael@0 80 // containing the values in the counter() or counters() in the order
michael@0 81 // given in the CSS spec.
michael@0 82 nsRefPtr<nsCSSValue::Array> mCounterStyle;
michael@0 83
michael@0 84 // false for counter(), true for counters()
michael@0 85 bool mAllCounters;
michael@0 86
michael@0 87 // args go directly to member variables here and of nsGenConNode
michael@0 88 nsCounterUseNode(nsCSSValue::Array* aCounterStyle,
michael@0 89 uint32_t aContentIndex, bool aAllCounters)
michael@0 90 : nsCounterNode(aContentIndex, USE)
michael@0 91 , mCounterStyle(aCounterStyle)
michael@0 92 , mAllCounters(aAllCounters)
michael@0 93 {
michael@0 94 NS_ASSERTION(aContentIndex <= INT32_MAX, "out of range");
michael@0 95 }
michael@0 96
michael@0 97 virtual bool InitTextFrame(nsGenConList* aList,
michael@0 98 nsIFrame* aPseudoFrame, nsIFrame* aTextFrame) MOZ_OVERRIDE;
michael@0 99
michael@0 100 // assign the correct |mValueAfter| value to a node that has been inserted
michael@0 101 // Should be called immediately after calling |Insert|.
michael@0 102 void Calc(nsCounterList* aList);
michael@0 103
michael@0 104 // The text that should be displayed for this counter.
michael@0 105 void GetText(nsString& aResult);
michael@0 106 };
michael@0 107
michael@0 108 struct nsCounterChangeNode : public nsCounterNode {
michael@0 109 int32_t mChangeValue; // the numeric value of the increment or reset
michael@0 110
michael@0 111 // |aPseudoFrame| is not necessarily a pseudo-element's frame, but
michael@0 112 // since it is for every other subclass of nsGenConNode, we follow
michael@0 113 // the naming convention here.
michael@0 114 // |aPropIndex| is the index of the value within the list in the
michael@0 115 // 'counter-increment' or 'counter-reset' property.
michael@0 116 nsCounterChangeNode(nsIFrame* aPseudoFrame,
michael@0 117 nsCounterNode::Type aChangeType,
michael@0 118 int32_t aChangeValue,
michael@0 119 int32_t aPropIndex)
michael@0 120 : nsCounterNode(// Fake a content index for resets and increments
michael@0 121 // that comes before all the real content, with
michael@0 122 // the resets first, in order, and then the increments.
michael@0 123 aPropIndex + (aChangeType == RESET
michael@0 124 ? (INT32_MIN)
michael@0 125 : (INT32_MIN / 2)),
michael@0 126 aChangeType)
michael@0 127 , mChangeValue(aChangeValue)
michael@0 128 {
michael@0 129 NS_ASSERTION(aPropIndex >= 0, "out of range");
michael@0 130 NS_ASSERTION(aChangeType == INCREMENT || aChangeType == RESET,
michael@0 131 "bad type");
michael@0 132 mPseudoFrame = aPseudoFrame;
michael@0 133 CheckFrameAssertions();
michael@0 134 }
michael@0 135
michael@0 136 // assign the correct |mValueAfter| value to a node that has been inserted
michael@0 137 // Should be called immediately after calling |Insert|.
michael@0 138 void Calc(nsCounterList* aList);
michael@0 139 };
michael@0 140
michael@0 141 inline nsCounterUseNode* nsCounterNode::UseNode()
michael@0 142 {
michael@0 143 NS_ASSERTION(mType == USE, "wrong type");
michael@0 144 return static_cast<nsCounterUseNode*>(this);
michael@0 145 }
michael@0 146
michael@0 147 inline nsCounterChangeNode* nsCounterNode::ChangeNode()
michael@0 148 {
michael@0 149 NS_ASSERTION(mType == INCREMENT || mType == RESET, "wrong type");
michael@0 150 return static_cast<nsCounterChangeNode*>(this);
michael@0 151 }
michael@0 152
michael@0 153 inline void nsCounterNode::Calc(nsCounterList* aList)
michael@0 154 {
michael@0 155 if (mType == USE)
michael@0 156 UseNode()->Calc(aList);
michael@0 157 else
michael@0 158 ChangeNode()->Calc(aList);
michael@0 159 }
michael@0 160
michael@0 161 class nsCounterList : public nsGenConList {
michael@0 162 public:
michael@0 163 nsCounterList() : nsGenConList(),
michael@0 164 mDirty(false)
michael@0 165 {}
michael@0 166
michael@0 167 void Insert(nsCounterNode* aNode) {
michael@0 168 nsGenConList::Insert(aNode);
michael@0 169 // Don't SetScope if we're dirty -- we'll reset all the scopes anyway,
michael@0 170 // and we can't usefully compute scopes right now.
michael@0 171 if (MOZ_LIKELY(!IsDirty())) {
michael@0 172 SetScope(aNode);
michael@0 173 }
michael@0 174 }
michael@0 175
michael@0 176 nsCounterNode* First() {
michael@0 177 return static_cast<nsCounterNode*>(mFirstNode);
michael@0 178 }
michael@0 179
michael@0 180 static nsCounterNode* Next(nsCounterNode* aNode) {
michael@0 181 return static_cast<nsCounterNode*>(nsGenConList::Next(aNode));
michael@0 182 }
michael@0 183 static nsCounterNode* Prev(nsCounterNode* aNode) {
michael@0 184 return static_cast<nsCounterNode*>(nsGenConList::Prev(aNode));
michael@0 185 }
michael@0 186
michael@0 187 static int32_t ValueBefore(nsCounterNode* aNode) {
michael@0 188 return aNode->mScopePrev ? aNode->mScopePrev->mValueAfter : 0;
michael@0 189 }
michael@0 190
michael@0 191 // Correctly set |aNode->mScopeStart| and |aNode->mScopePrev|
michael@0 192 void SetScope(nsCounterNode *aNode);
michael@0 193
michael@0 194 // Recalculate |mScopeStart|, |mScopePrev|, and |mValueAfter| for
michael@0 195 // all nodes and update text in text content nodes.
michael@0 196 void RecalcAll();
michael@0 197
michael@0 198 bool IsDirty() { return mDirty; }
michael@0 199 void SetDirty() { mDirty = true; }
michael@0 200
michael@0 201 private:
michael@0 202 bool mDirty;
michael@0 203 };
michael@0 204
michael@0 205 /**
michael@0 206 * The counter manager maintains an |nsCounterList| for each named
michael@0 207 * counter to keep track of all scopes with that name.
michael@0 208 */
michael@0 209 class nsCounterManager {
michael@0 210 public:
michael@0 211 nsCounterManager();
michael@0 212 // Returns true if dirty
michael@0 213 bool AddCounterResetsAndIncrements(nsIFrame *aFrame);
michael@0 214
michael@0 215 // Gets the appropriate counter list, creating it if necessary.
michael@0 216 // Returns null only on out-of-memory.
michael@0 217 nsCounterList* CounterListFor(const nsSubstring& aCounterName);
michael@0 218
michael@0 219 // Clean up data in any dirty counter lists.
michael@0 220 void RecalcAll();
michael@0 221
michael@0 222 // Destroy nodes for the frame in any lists, and return whether any
michael@0 223 // nodes were destroyed.
michael@0 224 bool DestroyNodesFor(nsIFrame *aFrame);
michael@0 225
michael@0 226 // Clear all data.
michael@0 227 void Clear() { mNames.Clear(); }
michael@0 228
michael@0 229 #ifdef DEBUG
michael@0 230 void Dump();
michael@0 231 #endif
michael@0 232
michael@0 233 static int32_t IncrementCounter(int32_t aOldValue, int32_t aIncrement)
michael@0 234 {
michael@0 235 // Addition of unsigned values is defined to be arithmetic
michael@0 236 // modulo 2^bits (C++ 2011, 3.9.1 [basic.fundamental], clause 4);
michael@0 237 // addition of signed values is undefined (and clang does
michael@0 238 // something very strange if we use it here). Likewise integral
michael@0 239 // conversion from signed to unsigned is also defined as modulo
michael@0 240 // 2^bits (C++ 2011, 4.7 [conv.integral], clause 2); conversion
michael@0 241 // from unsigned to signed is however undefined (ibid., clause 3),
michael@0 242 // but to do what we want we must nonetheless depend on that
michael@0 243 // small piece of undefined behavior.
michael@0 244 int32_t newValue = int32_t(uint32_t(aOldValue) + uint32_t(aIncrement));
michael@0 245 // The CSS Working Group resolved that a counter-increment that
michael@0 246 // exceeds internal limits should not increment at all.
michael@0 247 // http://lists.w3.org/Archives/Public/www-style/2013Feb/0392.html
michael@0 248 // (This means, for example, that if aIncrement is 5, the
michael@0 249 // counter will get stuck at the largest multiple of 5 less than
michael@0 250 // the maximum 32-bit integer.)
michael@0 251 if ((aIncrement > 0) != (newValue > aOldValue)) {
michael@0 252 newValue = aOldValue;
michael@0 253 }
michael@0 254 return newValue;
michael@0 255 }
michael@0 256
michael@0 257 private:
michael@0 258 // for |AddCounterResetsAndIncrements| only
michael@0 259 bool AddResetOrIncrement(nsIFrame *aFrame, int32_t aIndex,
michael@0 260 const nsStyleCounterData *aCounterData,
michael@0 261 nsCounterNode::Type aType);
michael@0 262
michael@0 263 nsClassHashtable<nsStringHashKey, nsCounterList> mNames;
michael@0 264 };
michael@0 265
michael@0 266 #endif /* nsCounterManager_h_ */

mercurial