Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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_ */ |