layout/base/nsCounterManager.h

changeset 0
6474c204b198
     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_ */

mercurial