Back to index

lightning-sunbird  0.9+nobinonly
nsCounterManager.h
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 // vim:cindent:ai:sw=4:ts=4:et:
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is nsCounterManager.
00017  *
00018  * The Initial Developer of the Original Code is the Mozilla Foundation.
00019  * Portions created by the Initial Developer are Copyright (C) 2004
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   L. David Baron <dbaron@dbaron.org> (original author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 #ifndef nsCounterManager_h_
00039 #define nsCounterManager_h_
00040 
00041 #include "nsGenConList.h"
00042 #include "nsAutoPtr.h"
00043 #include "nsClassHashtable.h"
00044 
00045 class nsCounterList;
00046 struct nsCounterUseNode;
00047 struct nsCounterChangeNode;
00048 
00049 struct nsCounterNode : public nsGenConNode {
00050     enum Type {
00051         RESET,     // a "counter number" pair in 'counter-reset'
00052         INCREMENT, // a "counter number" pair in 'counter-increment'
00053         USE        // counter() or counters() in 'content'
00054     };
00055     
00056     Type mType;
00057 
00058     // Counter value after this node
00059     PRInt32 mValueAfter;
00060 
00061     // mScopeStart points to the node (usually a RESET, but not in the
00062     // case of an implied 'counter-reset') that created the scope for
00063     // this element (for a RESET, its outer scope, i.e., the one it is
00064     // inside rather than the one it creates).
00065 
00066     // May be null for all types, but only when mScopePrev is also null.
00067     // Being null for a non-RESET means that it is an implied
00068     // 'counter-reset'.  Being null for a RESET means it has no outer
00069     // scope.
00070     nsCounterNode *mScopeStart;
00071 
00072     // mScopePrev points to the previous node that is in the same scope,
00073     // or for a RESET, the previous node in the scope outside of the
00074     // reset.
00075 
00076     // May be null for all types, but only when mScopeStart is also
00077     // null.  Following the mScopePrev links will eventually lead to
00078     // mScopeStart.  Being null for a non-RESET means that it is an
00079     // implied 'counter-reset'.  Being null for a RESET means it has no
00080     // outer scope.
00081     nsCounterNode *mScopePrev;
00082 
00083     inline nsCounterUseNode* UseNode();
00084     inline nsCounterChangeNode* ChangeNode();
00085 
00086     // For RESET and INCREMENT nodes, aPseudoFrame need not be a
00087     // pseudo-element, and aContentIndex represents the index within the
00088     // 'counter-reset' or 'counter-increment' property instead of within
00089     // the 'content' property but offset to ensure that (reset,
00090     // increment, use) sort in that order.  (This slight weirdness
00091     // allows sharing a lot of code with 'quotes'.)
00092     nsCounterNode(nsIFrame* aPseudoFrame, PRInt32 aContentIndex, Type aType)
00093         : nsGenConNode(aPseudoFrame, aContentIndex)
00094         , mType(aType)
00095         , mValueAfter(0)
00096         , mScopeStart(nsnull)
00097         , mScopePrev(nsnull)
00098     {
00099     }
00100 
00101     // to avoid virtual function calls in the common case
00102     inline void Calc(nsCounterList* aList);
00103 };
00104 
00105 struct nsCounterUseNode : public nsCounterNode {
00106     // The same structure passed through the style system:  an array
00107     // containing the values in the counter() or counters() in the order
00108     // given in the CSS spec.
00109     nsRefPtr<nsCSSValue::Array> mCounterStyle;
00110 
00111     // false for counter(), true for counters()
00112     PRBool mAllCounters;
00113 
00114     // args go directly to member variables here and of nsGenConNode
00115     nsCounterUseNode(nsCSSValue::Array* aCounterStyle, nsIFrame* aPseudoFrame,
00116                      PRUint32 aContentIndex, PRBool aAllCounters)
00117         : nsCounterNode(aPseudoFrame, aContentIndex, USE)
00118         , mCounterStyle(aCounterStyle)
00119         , mAllCounters(aAllCounters)
00120     {
00121         NS_ASSERTION(aContentIndex >= 0, "out of range");
00122     }
00123 
00124     // assign the correct |mValueAfter| value to a node that has been inserted
00125     // Should be called immediately after calling |Insert|.
00126     void Calc(nsCounterList* aList);
00127 
00128     // The text that should be displayed for this counter.
00129     void GetText(nsString& aResult);
00130 };
00131 
00132 struct nsCounterChangeNode : public nsCounterNode {
00133     PRInt32 mChangeValue; // the numeric value of the increment or reset
00134 
00135     // |aPseudoFrame| is not necessarily a pseudo-element's frame, but
00136     // since it is for every other subclass of nsGenConNode, we follow
00137     // the naming convention here.
00138     // |aPropIndex| is the index of the value within the list in the
00139     // 'counter-increment' or 'counter-reset' property.
00140     nsCounterChangeNode(nsIFrame* aPseudoFrame,
00141                         nsCounterNode::Type aChangeType,
00142                         PRInt32 aChangeValue,
00143                         PRInt32 aPropIndex)
00144         : nsCounterNode(aPseudoFrame,
00145                         // Fake a content index for resets and increments
00146                         // that comes before all the real content, with
00147                         // the resets first, in order, and then the increments.
00148                         aPropIndex + (aChangeType == RESET
00149                                         ? (PR_INT32_MIN) 
00150                                         : (PR_INT32_MIN / 2)),
00151                         aChangeType)
00152         , mChangeValue(aChangeValue)
00153     {
00154         NS_ASSERTION(aPropIndex >= 0, "out of range");
00155         NS_ASSERTION(aChangeType == INCREMENT || aChangeType == RESET,
00156                      "bad type");
00157     }
00158 
00159     // assign the correct |mValueAfter| value to a node that has been inserted
00160     // Should be called immediately after calling |Insert|.
00161     void Calc(nsCounterList* aList);
00162 };
00163 
00164 inline nsCounterUseNode* nsCounterNode::UseNode()
00165 {
00166     NS_ASSERTION(mType == USE, "wrong type");
00167     return NS_STATIC_CAST(nsCounterUseNode*, this);
00168 }
00169 
00170 inline nsCounterChangeNode* nsCounterNode::ChangeNode()
00171 {
00172     NS_ASSERTION(mType == INCREMENT || mType == RESET, "wrong type");
00173     return NS_STATIC_CAST(nsCounterChangeNode*, this);
00174 }
00175 
00176 inline void nsCounterNode::Calc(nsCounterList* aList)
00177 {
00178     if (mType == USE)
00179         UseNode()->Calc(aList);
00180     else
00181         ChangeNode()->Calc(aList);
00182 }
00183 
00184 class nsCounterList : public nsGenConList {
00185 public:
00186     nsCounterList() : nsGenConList(),
00187                       mDirty(PR_FALSE)
00188     {}
00189 
00190     void Insert(nsCounterNode* aNode) {
00191         nsGenConList::Insert(aNode);
00192         // Don't SetScope if we're dirty -- we'll reset all the scopes anyway,
00193         // and we can't usefully compute scopes right now.
00194         if (NS_LIKELY(!IsDirty())) {
00195             SetScope(aNode);
00196         }
00197     }
00198 
00199     nsCounterNode* First() {
00200         return NS_STATIC_CAST(nsCounterNode*, mFirstNode);
00201     }
00202 
00203     static nsCounterNode* Next(nsCounterNode* aNode) {
00204         return NS_STATIC_CAST(nsCounterNode*, nsGenConList::Next(aNode));
00205     }
00206     static nsCounterNode* Prev(nsCounterNode* aNode) {
00207         return NS_STATIC_CAST(nsCounterNode*, nsGenConList::Prev(aNode));
00208     }
00209 
00210     static PRInt32 ValueBefore(nsCounterNode* aNode) {
00211         return aNode->mScopePrev ? aNode->mScopePrev->mValueAfter : 0;
00212     }
00213 
00214     // Correctly set |aNode->mScopeStart| and |aNode->mScopePrev|
00215     void SetScope(nsCounterNode *aNode);
00216   
00217     // Recalculate |mScopeStart|, |mScopePrev|, and |mValueAfter| for
00218     // all nodes and update text in text content nodes.
00219     void RecalcAll();
00220 
00221     PRBool IsDirty() { return mDirty; }
00222     void SetDirty() { mDirty = PR_TRUE; }
00223 
00224 private:
00225     PRBool mDirty;
00226 };
00227 
00232 class nsCounterManager {
00233 public:
00234     nsCounterManager();
00235     // Returns true if dirty
00236     PRBool AddCounterResetsAndIncrements(nsIFrame *aFrame);
00237 
00238     // Gets the appropriate counter list, creating it if necessary.
00239     // Returns null only on out-of-memory.
00240     nsCounterList* CounterListFor(const nsSubstring& aCounterName);
00241 
00242     // Clean up data in any dirty counter lists.
00243     void RecalcAll();
00244 
00245     // Destroy nodes for the frame in any lists, and return whether any
00246     // nodes were destroyed.
00247     PRBool DestroyNodesFor(nsIFrame *aFrame);
00248 
00249     // Clear all data.
00250     void Clear() { mNames.Clear(); }
00251 
00252 #ifdef DEBUG
00253     void Dump();
00254 #endif
00255 
00256 private:
00257     // for |AddCounterResetsAndIncrements| only
00258     PRBool AddResetOrIncrement(nsIFrame *aFrame, PRInt32 aIndex,
00259                                const nsStyleCounterData *aCounterData,
00260                                nsCounterNode::Type aType);
00261 
00262     nsClassHashtable<nsStringHashKey, nsCounterList> mNames;
00263 };
00264 
00265 #endif /* nsCounterManager_h_ */