tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsCounterManager.h (12922B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 // vim:cindent:ai:sw=4:ts=4:et:
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /* implementation of CSS counters (for numbering things) */
      8 
      9 #ifndef nsCounterManager_h_
     10 #define nsCounterManager_h_
     11 
     12 #include "CounterStyleManager.h"
     13 #include "mozilla/Likely.h"
     14 #include "nsClassHashtable.h"
     15 #include "nsGenConList.h"
     16 
     17 class nsCounterList;
     18 struct nsCounterUseNode;
     19 struct nsCounterChangeNode;
     20 
     21 namespace mozilla {
     22 
     23 class ContainStyleScope;
     24 
     25 }  // namespace mozilla
     26 
     27 struct nsCounterNode : public nsGenConNode {
     28  enum Type {
     29    RESET,      // a "counter number" pair in 'counter-reset'
     30    INCREMENT,  // a "counter number" pair in 'counter-increment'
     31    SET,        // a "counter number" pair in 'counter-set'
     32    USE         // counter() or counters() in 'content'
     33  };
     34 
     35  Type mType;
     36 
     37  // Counter value after this node
     38  int32_t mValueAfter = 0;
     39 
     40  // mScopeStart points to the node (usually a RESET, but not in the
     41  // case of an implied 'counter-reset') that created the scope for
     42  // this element (for a RESET, its outer scope, i.e., the one it is
     43  // inside rather than the one it creates).
     44 
     45  // May be null for all types, but only when mScopePrev is also null.
     46  // Being null for a non-RESET means that it is an implied
     47  // 'counter-reset'.  Being null for a RESET means it has no outer
     48  // scope.
     49  nsCounterNode* mScopeStart = nullptr;
     50 
     51  // mScopePrev points to the previous node that is in the same scope,
     52  // or for a RESET, the previous node in the scope outside of the
     53  // reset.
     54 
     55  // May be null for all types, but only when mScopeStart is also
     56  // null.  Following the mScopePrev links will eventually lead to
     57  // mScopeStart.  Being null for a non-RESET means that it is an
     58  // implied 'counter-reset'.  Being null for a RESET means it has no
     59  // outer scope.
     60  nsCounterNode* mScopePrev = nullptr;
     61 
     62  // Whether or not this node's scope crosses `contain: style` boundaries.
     63  // This can happen for USE nodes that come before any other types of
     64  // nodes in a `contain: style` boundary's list.
     65  bool mCrossesContainStyleBoundaries = false;
     66 
     67  inline nsCounterUseNode* UseNode();
     68  inline nsCounterChangeNode* ChangeNode();
     69 
     70  // For RESET, INCREMENT and SET nodes, aPseudoFrame need not be a
     71  // pseudo-element, and aContentIndex represents the index within the
     72  // 'counter-reset', 'counter-increment' or 'counter-set'  property
     73  // instead of within the 'content' property but offset to ensure
     74  // that (reset, increment, set, use) sort in that order.
     75  // It is zero for legacy bullet USE counter nodes.
     76  // (This slight weirdness allows sharing a lot of code with 'quotes'.)
     77  nsCounterNode(int32_t aContentIndex, Type aType)
     78      : nsGenConNode(aContentIndex), mType(aType) {}
     79 
     80  // to avoid virtual function calls in the common case
     81  inline void Calc(nsCounterList* aList, bool aNotify);
     82 
     83  // Is this a RESET node for a content-based (i.e. without a start value)
     84  // reversed() counter?
     85  inline bool IsContentBasedReset();
     86 
     87  // Is this a RESET node for a reversed() counter?
     88  inline bool IsReversed();
     89 
     90  // Is this an INCREMENT node that needs to be initialized to -1 or 1
     91  // depending on if our scope is reversed() or not?
     92  inline bool IsUnitializedIncrementNode();
     93 };
     94 
     95 struct nsCounterUseNode : public nsCounterNode {
     96  mozilla::StyleCounterStyle mCounterStyle;
     97  nsString mSeparator;
     98 
     99  // false for counter(), true for counters()
    100  bool mAllCounters = false;
    101 
    102  bool mForLegacyBullet = false;
    103 
    104  enum ForLegacyBullet { ForLegacyBullet };
    105  nsCounterUseNode(enum ForLegacyBullet,
    106                   const mozilla::StyleCounterStyle& aCounterStyle)
    107      : nsCounterNode(0, USE),
    108        mCounterStyle(aCounterStyle),
    109        mForLegacyBullet(true) {}
    110 
    111  // args go directly to member variables here and of nsGenConNode
    112  nsCounterUseNode(const mozilla::StyleCounterStyle& aCounterStyle,
    113                   nsString aSeparator, uint32_t aContentIndex,
    114                   bool aAllCounters)
    115      : nsCounterNode(aContentIndex, USE),
    116        mCounterStyle(aCounterStyle),
    117        mSeparator(std::move(aSeparator)),
    118        mAllCounters(aAllCounters) {
    119    NS_ASSERTION(aContentIndex <= INT32_MAX, "out of range");
    120  }
    121 
    122  bool InitTextFrame(nsGenConList* aList, nsIFrame* aPseudoFrame,
    123                     nsIFrame* aTextFrame) override;
    124 
    125  // assign the correct |mValueAfter| value to a node that has been inserted,
    126  // and update the value of the text node, notifying if `aNotify` is true.
    127  // Should be called immediately after calling |Insert|.
    128  void Calc(nsCounterList* aList, bool aNotify);
    129 
    130  // The text that should be displayed for this counter.
    131  void GetText(nsString& aResult);
    132  void GetText(mozilla::WritingMode aWM, mozilla::CounterStyle* aStyle,
    133               nsString& aResult);
    134 };
    135 
    136 struct nsCounterChangeNode : public nsCounterNode {
    137  // |aPseudoFrame| is not necessarily a pseudo-element's frame, but
    138  // since it is for every other subclass of nsGenConNode, we follow
    139  // the naming convention here.
    140  // |aPropIndex| is the index of the value within the list in the
    141  // 'counter-increment', 'counter-reset' or 'counter-set' property.
    142  nsCounterChangeNode(nsIFrame* aPseudoFrame, nsCounterNode::Type aChangeType,
    143                      int32_t aChangeValue, int32_t aPropIndex,
    144                      bool aIsReversed)
    145      : nsCounterNode(  // Fake a content index for resets, increments and sets
    146                        // that comes before all the real content, with
    147                        // the resets first, in order, and then the increments
    148                        // and then the sets.
    149            aPropIndex + (aChangeType == RESET ? (INT32_MIN)
    150                                               : (aChangeType == INCREMENT
    151                                                      ? ((INT32_MIN / 3) * 2)
    152                                                      : INT32_MIN / 3)),
    153            aChangeType),
    154        mChangeValue(aChangeValue),
    155        mIsReversed(aIsReversed),
    156        mSeenSetNode(false) {
    157    NS_ASSERTION(aPropIndex >= 0, "out of range");
    158    NS_ASSERTION(
    159        aChangeType == INCREMENT || aChangeType == SET || aChangeType == RESET,
    160        "bad type");
    161    mPseudoFrame = aPseudoFrame;
    162    CheckFrameAssertions();
    163  }
    164 
    165  // assign the correct |mValueAfter| value to a node that has been inserted
    166  // Should be called immediately after calling |Insert|.
    167  void Calc(nsCounterList* aList);
    168 
    169  // The numeric value of the INCREMENT, SET or RESET.
    170  // Note: numeric_limits<int32_t>::min() is used for content-based reversed()
    171  // RESET nodes, and temporarily on INCREMENT nodes to signal that it should be
    172  // initialized to -1 or 1 depending on if the scope is reversed() or not.
    173  int32_t mChangeValue;
    174 
    175  // True if the counter is reversed(). Only used on RESET nodes.
    176  bool mIsReversed : 1;
    177  // True if we've seen a SET node during the initialization of
    178  // an IsContentBasedReset() node; always false on other nodes.
    179  bool mSeenSetNode : 1;
    180 };
    181 
    182 inline nsCounterUseNode* nsCounterNode::UseNode() {
    183  NS_ASSERTION(mType == USE, "wrong type");
    184  return static_cast<nsCounterUseNode*>(this);
    185 }
    186 
    187 inline nsCounterChangeNode* nsCounterNode::ChangeNode() {
    188  MOZ_ASSERT(mType == INCREMENT || mType == SET || mType == RESET);
    189  return static_cast<nsCounterChangeNode*>(this);
    190 }
    191 
    192 inline void nsCounterNode::Calc(nsCounterList* aList, bool aNotify) {
    193  if (mType == USE) {
    194    UseNode()->Calc(aList, aNotify);
    195  } else {
    196    ChangeNode()->Calc(aList);
    197  }
    198 }
    199 
    200 inline bool nsCounterNode::IsContentBasedReset() {
    201  return mType == RESET &&
    202         ChangeNode()->mChangeValue == std::numeric_limits<int32_t>::min();
    203 }
    204 
    205 inline bool nsCounterNode::IsReversed() {
    206  return mType == RESET && ChangeNode()->mIsReversed;
    207 }
    208 
    209 inline bool nsCounterNode::IsUnitializedIncrementNode() {
    210  return mType == INCREMENT &&
    211         ChangeNode()->mChangeValue == std::numeric_limits<int32_t>::min();
    212 }
    213 
    214 class nsCounterList : public nsGenConList {
    215 public:
    216  nsCounterList(nsAtom* aCounterName, mozilla::ContainStyleScope* aScope)
    217      : mCounterName(aCounterName), mScope(aScope) {
    218    MOZ_ASSERT(aScope);
    219  }
    220 
    221 #if defined(DEBUG) || defined(MOZ_LAYOUT_DEBUGGER)
    222  void Dump();
    223 #endif
    224 
    225  // Return the first node for aFrame on this list, or nullptr.
    226  nsCounterNode* GetFirstNodeFor(nsIFrame* aFrame) const {
    227    return static_cast<nsCounterNode*>(nsGenConList::GetFirstNodeFor(aFrame));
    228  }
    229 
    230  void Insert(nsCounterNode* aNode) {
    231    nsGenConList::Insert(aNode);
    232    // Don't SetScope if we're dirty -- we'll reset all the scopes anyway,
    233    // and we can't usefully compute scopes right now.
    234    if (MOZ_LIKELY(!IsDirty())) {
    235      SetScope(aNode);
    236    }
    237  }
    238 
    239  nsCounterNode* First() {
    240    return static_cast<nsCounterNode*>(mList.getFirst());
    241  }
    242 
    243  static nsCounterNode* Next(nsCounterNode* aNode) {
    244    return static_cast<nsCounterNode*>(nsGenConList::Next(aNode));
    245  }
    246  static nsCounterNode* Prev(nsCounterNode* aNode) {
    247    return static_cast<nsCounterNode*>(nsGenConList::Prev(aNode));
    248  }
    249 
    250  static int32_t ValueBefore(nsCounterNode* aNode) {
    251    if (!aNode->mScopePrev) {
    252      return 0;
    253    }
    254 
    255    if (aNode->mType != nsCounterNode::USE &&
    256        aNode->mScopePrev->mCrossesContainStyleBoundaries) {
    257      return 0;
    258    }
    259 
    260    return aNode->mScopePrev->mValueAfter;
    261  }
    262 
    263  // Correctly set |aNode->mScopeStart| and |aNode->mScopePrev|
    264  void SetScope(nsCounterNode* aNode);
    265 
    266  // Recalculate |mScopeStart|, |mScopePrev|, and |mValueAfter| for
    267  // all nodes and update text in text content nodes.
    268  void RecalcAll();
    269 
    270  bool IsDirty() const;
    271  void SetDirty();
    272  bool IsRecalculatingAll() const { return mRecalculatingAll; }
    273 
    274 private:
    275  bool SetScopeByWalkingBackwardThroughList(
    276      nsCounterNode* aNodeToSetScopeFor, const nsIContent* aNodeContent,
    277      nsCounterNode* aNodeToBeginLookingAt);
    278 
    279  RefPtr<nsAtom> mCounterName;
    280  mozilla::ContainStyleScope* mScope;
    281  bool mRecalculatingAll = false;
    282 };
    283 
    284 /**
    285 * The counter manager maintains an |nsCounterList| for each named
    286 * counter to keep track of all scopes with that name.
    287 */
    288 class nsCounterManager {
    289 public:
    290  explicit nsCounterManager(mozilla::ContainStyleScope* scope)
    291      : mScope(scope) {}
    292 
    293  // Returns true if dirty
    294  bool AddCounterChanges(nsIFrame* aFrame);
    295 
    296  // Gets the appropriate counter list, creating it if necessary.
    297  // Guaranteed to return non-null. (Uses an infallible hashtable API.)
    298  nsCounterList* GetOrCreateCounterList(nsAtom* aCounterName);
    299 
    300  // Gets the appropriate counter list, returning null if it doesn't exist.
    301  nsCounterList* GetCounterList(nsAtom* aCounterName);
    302 
    303  // Clean up data in any dirty counter lists.
    304  void RecalcAll();
    305 
    306  // Set all counter lists dirty
    307  void SetAllDirty();
    308 
    309  // Destroy nodes for the frame in any lists, and return whether any
    310  // nodes were destroyed.
    311  bool DestroyNodesFor(nsIFrame* aFrame);
    312 
    313  // Clear all data.
    314  void Clear() { mNames.Clear(); }
    315 
    316 #ifdef ACCESSIBILITY
    317  // Set |aOrdinal| to the first used counter value for the given frame and
    318  // return true. If no USE node for the given frame can be found, return false
    319  // and do not change the value of |aOrdinal|.
    320  bool GetFirstCounterValueForFrame(nsIFrame* aFrame,
    321                                    mozilla::CounterValue& aOrdinal) const;
    322 #endif
    323 
    324 #if defined(DEBUG) || defined(MOZ_LAYOUT_DEBUGGER)
    325  void Dump() const;
    326 #endif
    327 
    328  static int32_t IncrementCounter(int32_t aOldValue, int32_t aIncrement) {
    329    // Addition of unsigned values is defined to be arithmetic
    330    // modulo 2^bits (C++ 2011, 3.9.1 [basic.fundamental], clause 4);
    331    // addition of signed values is undefined (and clang does
    332    // something very strange if we use it here).  Likewise integral
    333    // conversion from signed to unsigned is also defined as modulo
    334    // 2^bits (C++ 2011, 4.7 [conv.integral], clause 2); conversion
    335    // from unsigned to signed is however undefined (ibid., clause 3),
    336    // but to do what we want we must nonetheless depend on that
    337    // small piece of undefined behavior.
    338    int32_t newValue = int32_t(uint32_t(aOldValue) + uint32_t(aIncrement));
    339    // The CSS Working Group resolved that a counter-increment that
    340    // exceeds internal limits should not increment at all.
    341    // http://lists.w3.org/Archives/Public/www-style/2013Feb/0392.html
    342    // (This means, for example, that if aIncrement is 5, the
    343    // counter will get stuck at the largest multiple of 5 less than
    344    // the maximum 32-bit integer.)
    345    if ((aIncrement > 0) != (newValue > aOldValue)) {
    346      newValue = aOldValue;
    347    }
    348    return newValue;
    349  }
    350 
    351 private:
    352  mozilla::ContainStyleScope* mScope;
    353  nsClassHashtable<nsAtomHashKey, nsCounterList> mNames;
    354 };
    355 
    356 #endif /* nsCounterManager_h_ */