LCOV - code coverage report
Current view: directory - layout/base - nsCounterManager.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 151 0 0.0 %
Date: 2012-06-02 Functions: 17 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 : // vim:cindent:ai:sw=4:ts=4:et:
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is nsCounterManager.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is the Mozilla Foundation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2004
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   L. David Baron <dbaron@dbaron.org> (original author)
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : /* implementation of CSS counters (for numbering things) */
      40                 : 
      41                 : #include "nsCounterManager.h"
      42                 : #include "nsBulletFrame.h" // legacy location for list style type to text code
      43                 : #include "nsContentUtils.h"
      44                 : #include "nsTArray.h"
      45                 : 
      46                 : bool
      47               0 : nsCounterUseNode::InitTextFrame(nsGenConList* aList,
      48                 :         nsIFrame* aPseudoFrame, nsIFrame* aTextFrame)
      49                 : {
      50               0 :   nsCounterNode::InitTextFrame(aList, aPseudoFrame, aTextFrame);
      51                 : 
      52               0 :   nsCounterList *counterList = static_cast<nsCounterList*>(aList);
      53               0 :   counterList->Insert(this);
      54               0 :   bool dirty = counterList->IsDirty();
      55               0 :   if (!dirty) {
      56               0 :     if (counterList->IsLast(this)) {
      57               0 :       Calc(counterList);
      58               0 :       nsAutoString contentString;
      59               0 :       GetText(contentString);
      60               0 :       aTextFrame->GetContent()->SetText(contentString, false);
      61                 :     } else {
      62                 :       // In all other cases (list already dirty or node not at the end),
      63                 :       // just start with an empty string for now and when we recalculate
      64                 :       // the list we'll change the value to the right one.
      65               0 :       counterList->SetDirty();
      66               0 :       return true;
      67                 :     }
      68                 :   }
      69                 :   
      70               0 :   return false;
      71                 : }
      72                 : 
      73                 : // assign the correct |mValueAfter| value to a node that has been inserted
      74                 : // Should be called immediately after calling |Insert|.
      75               0 : void nsCounterUseNode::Calc(nsCounterList *aList)
      76                 : {
      77               0 :     NS_ASSERTION(!aList->IsDirty(),
      78                 :                  "Why are we calculating with a dirty list?");
      79               0 :     mValueAfter = aList->ValueBefore(this);
      80               0 : }
      81                 : 
      82                 : // assign the correct |mValueAfter| value to a node that has been inserted
      83                 : // Should be called immediately after calling |Insert|.
      84               0 : void nsCounterChangeNode::Calc(nsCounterList *aList)
      85                 : {
      86               0 :     NS_ASSERTION(!aList->IsDirty(),
      87                 :                  "Why are we calculating with a dirty list?");
      88               0 :     if (mType == RESET) {
      89               0 :         mValueAfter = mChangeValue;
      90                 :     } else {
      91               0 :         NS_ASSERTION(mType == INCREMENT, "invalid type");
      92               0 :         mValueAfter = aList->ValueBefore(this) + mChangeValue;
      93                 :     }
      94               0 : }
      95                 : 
      96                 : // The text that should be displayed for this counter.
      97                 : void
      98               0 : nsCounterUseNode::GetText(nsString& aResult)
      99                 : {
     100               0 :     aResult.Truncate();
     101                 : 
     102               0 :     nsAutoTArray<nsCounterNode*, 8> stack;
     103               0 :     stack.AppendElement(static_cast<nsCounterNode*>(this));
     104                 : 
     105               0 :     if (mAllCounters && mScopeStart)
     106               0 :         for (nsCounterNode *n = mScopeStart; n->mScopePrev; n = n->mScopeStart)
     107               0 :             stack.AppendElement(n->mScopePrev);
     108                 : 
     109               0 :     const nsCSSValue& styleItem = mCounterStyle->Item(mAllCounters ? 2 : 1);
     110               0 :     PRInt32 style = styleItem.GetIntValue();
     111                 :     const PRUnichar* separator;
     112               0 :     if (mAllCounters)
     113               0 :         separator = mCounterStyle->Item(1).GetStringBufferValue();
     114                 : 
     115               0 :     for (PRUint32 i = stack.Length() - 1;; --i) {
     116               0 :         nsCounterNode *n = stack[i];
     117               0 :         nsBulletFrame::AppendCounterText(style, n->mValueAfter, aResult);
     118               0 :         if (i == 0)
     119                 :             break;
     120               0 :         NS_ASSERTION(mAllCounters, "yikes, separator is uninitialized");
     121               0 :         aResult.Append(separator);
     122                 :     }
     123               0 : }
     124                 : 
     125                 : void
     126               0 : nsCounterList::SetScope(nsCounterNode *aNode)
     127                 : {
     128                 :     // This function is responsible for setting |mScopeStart| and
     129                 :     // |mScopePrev| (whose purpose is described in nsCounterManager.h).
     130                 :     // We do this by starting from the node immediately preceding
     131                 :     // |aNode| in content tree order, which is reasonably likely to be
     132                 :     // the previous element in our scope (or, for a reset, the previous
     133                 :     // element in the containing scope, which is what we want).  If
     134                 :     // we're not in the same scope that it is, then it's too deep in the
     135                 :     // frame tree, so we walk up parent scopes until we find something
     136                 :     // appropriate.
     137                 : 
     138               0 :     if (aNode == First()) {
     139               0 :         aNode->mScopeStart = nsnull;
     140               0 :         aNode->mScopePrev = nsnull;
     141               0 :         return;
     142                 :     }
     143                 : 
     144                 :     // Get the content node for aNode's rendering object's *parent*,
     145                 :     // since scope includes siblings, so we want a descendant check on
     146                 :     // parents.
     147               0 :     nsIContent *nodeContent = aNode->mPseudoFrame->GetContent()->GetParent();
     148                 : 
     149               0 :     for (nsCounterNode *prev = Prev(aNode), *start;
     150                 :          prev; prev = start->mScopePrev) {
     151                 :         // If |prev| starts a scope (because it's a real or implied
     152                 :         // reset), we want it as the scope start rather than the start
     153                 :         // of its enclosing scope.  Otherwise, there's no enclosing
     154                 :         // scope, so the next thing in prev's scope shares its scope
     155                 :         // start.
     156               0 :         start = (prev->mType == nsCounterNode::RESET || !prev->mScopeStart)
     157               0 :                   ? prev : prev->mScopeStart;
     158                 : 
     159                 :         // |startContent| is analogous to |nodeContent| (see above).
     160               0 :         nsIContent *startContent = start->mPseudoFrame->GetContent()->GetParent();
     161               0 :         NS_ASSERTION(nodeContent || !startContent,
     162                 :                      "null check on startContent should be sufficient to "
     163                 :                      "null check nodeContent as well, since if nodeContent "
     164                 :                      "is for the root, startContent (which is before it) "
     165                 :                      "must be too");
     166                 : 
     167                 :              // A reset's outer scope can't be a scope created by a sibling.
     168               0 :         if (!(aNode->mType == nsCounterNode::RESET &&
     169               0 :               nodeContent == startContent) &&
     170                 :               // everything is inside the root (except the case above,
     171                 :               // a second reset on the root)
     172                 :             (!startContent ||
     173                 :              nsContentUtils::ContentIsDescendantOf(nodeContent,
     174               0 :                                                    startContent))) {
     175               0 :             aNode->mScopeStart = start;
     176               0 :             aNode->mScopePrev  = prev;
     177               0 :             return;
     178                 :         }
     179                 :     }
     180                 : 
     181               0 :     aNode->mScopeStart = nsnull;
     182               0 :     aNode->mScopePrev  = nsnull;
     183                 : }
     184                 : 
     185                 : void
     186               0 : nsCounterList::RecalcAll()
     187                 : {
     188               0 :     mDirty = false;
     189                 : 
     190               0 :     nsCounterNode *node = First();
     191               0 :     if (!node)
     192               0 :         return;
     193                 : 
     194               0 :     do {
     195               0 :         SetScope(node);
     196               0 :         node->Calc(this);
     197                 : 
     198               0 :         if (node->mType == nsCounterNode::USE) {
     199               0 :             nsCounterUseNode *useNode = node->UseNode();
     200                 :             // Null-check mText, since if the frame constructor isn't
     201                 :             // batching, we could end up here while the node is being
     202                 :             // constructed.
     203               0 :             if (useNode->mText) {
     204               0 :                 nsAutoString text;
     205               0 :                 useNode->GetText(text);
     206               0 :                 useNode->mText->SetData(text);
     207                 :             }
     208                 :         }
     209               0 :     } while ((node = Next(node)) != First());
     210                 : }
     211                 : 
     212               0 : nsCounterManager::nsCounterManager()
     213                 : {
     214               0 :     mNames.Init(16);
     215               0 : }
     216                 : 
     217                 : bool
     218               0 : nsCounterManager::AddCounterResetsAndIncrements(nsIFrame *aFrame)
     219                 : {
     220               0 :     const nsStyleContent *styleContent = aFrame->GetStyleContent();
     221               0 :     if (!styleContent->CounterIncrementCount() &&
     222               0 :         !styleContent->CounterResetCount())
     223               0 :         return false;
     224                 : 
     225                 :     // Add in order, resets first, so all the comparisons will be optimized
     226                 :     // for addition at the end of the list.
     227                 :     PRInt32 i, i_end;
     228               0 :     bool dirty = false;
     229               0 :     for (i = 0, i_end = styleContent->CounterResetCount(); i != i_end; ++i)
     230                 :         dirty |= AddResetOrIncrement(aFrame, i,
     231                 :                                      styleContent->GetCounterResetAt(i),
     232               0 :                                      nsCounterChangeNode::RESET);
     233               0 :     for (i = 0, i_end = styleContent->CounterIncrementCount(); i != i_end; ++i)
     234                 :         dirty |= AddResetOrIncrement(aFrame, i,
     235                 :                                      styleContent->GetCounterIncrementAt(i),
     236               0 :                                      nsCounterChangeNode::INCREMENT);
     237               0 :     return dirty;
     238                 : }
     239                 : 
     240                 : bool
     241               0 : nsCounterManager::AddResetOrIncrement(nsIFrame *aFrame, PRInt32 aIndex,
     242                 :                                       const nsStyleCounterData *aCounterData,
     243                 :                                       nsCounterNode::Type aType)
     244                 : {
     245                 :     nsCounterChangeNode *node =
     246               0 :         new nsCounterChangeNode(aFrame, aType, aCounterData->mValue, aIndex);
     247                 : 
     248               0 :     nsCounterList *counterList = CounterListFor(aCounterData->mCounter);
     249               0 :     if (!counterList) {
     250               0 :         NS_NOTREACHED("CounterListFor failed (should only happen on OOM)");
     251               0 :         return false;
     252                 :     }
     253                 : 
     254               0 :     counterList->Insert(node);
     255               0 :     if (!counterList->IsLast(node)) {
     256                 :         // Tell the caller it's responsible for recalculating the entire
     257                 :         // list.
     258               0 :         counterList->SetDirty();
     259               0 :         return true;
     260                 :     }
     261                 : 
     262                 :     // Don't call Calc() if the list is already dirty -- it'll be recalculated
     263                 :     // anyway, and trying to calculate with a dirty list doesn't work.
     264               0 :     if (NS_LIKELY(!counterList->IsDirty())) {
     265               0 :         node->Calc(counterList);
     266                 :     }
     267               0 :     return false;
     268                 : }
     269                 : 
     270                 : nsCounterList*
     271               0 : nsCounterManager::CounterListFor(const nsSubstring& aCounterName)
     272                 : {
     273                 :     // XXX Why doesn't nsTHashtable provide an API that allows us to use
     274                 :     // get/put in one hashtable lookup?
     275                 :     nsCounterList *counterList;
     276               0 :     if (!mNames.Get(aCounterName, &counterList)) {
     277               0 :         counterList = new nsCounterList();
     278               0 :         if (!mNames.Put(aCounterName, counterList)) {
     279               0 :             delete counterList;
     280               0 :             return nsnull;
     281                 :         }
     282                 :     }
     283               0 :     return counterList;
     284                 : }
     285                 : 
     286                 : static PLDHashOperator
     287               0 : RecalcDirtyLists(const nsAString& aKey, nsCounterList* aList, void* aClosure)
     288                 : {
     289               0 :     if (aList->IsDirty())
     290               0 :         aList->RecalcAll();
     291               0 :     return PL_DHASH_NEXT;
     292                 : }
     293                 : 
     294                 : void
     295               0 : nsCounterManager::RecalcAll()
     296                 : {
     297               0 :     mNames.EnumerateRead(RecalcDirtyLists, nsnull);
     298               0 : }
     299                 : 
     300                 : struct DestroyNodesData {
     301               0 :     DestroyNodesData(nsIFrame *aFrame)
     302                 :         : mFrame(aFrame)
     303               0 :         , mDestroyedAny(false)
     304                 :     {
     305               0 :     }
     306                 : 
     307                 :     nsIFrame *mFrame;
     308                 :     bool mDestroyedAny;
     309                 : };
     310                 : 
     311                 : static PLDHashOperator
     312               0 : DestroyNodesInList(const nsAString& aKey, nsCounterList* aList, void* aClosure)
     313                 : {
     314               0 :     DestroyNodesData *data = static_cast<DestroyNodesData*>(aClosure);
     315               0 :     if (aList->DestroyNodesFor(data->mFrame)) {
     316               0 :         data->mDestroyedAny = true;
     317               0 :         aList->SetDirty();
     318                 :     }
     319               0 :     return PL_DHASH_NEXT;
     320                 : }
     321                 : 
     322                 : bool
     323               0 : nsCounterManager::DestroyNodesFor(nsIFrame *aFrame)
     324                 : {
     325               0 :     DestroyNodesData data(aFrame);
     326               0 :     mNames.EnumerateRead(DestroyNodesInList, &data);
     327               0 :     return data.mDestroyedAny;
     328                 : }
     329                 : 
     330                 : #ifdef DEBUG
     331                 : static PLDHashOperator
     332               0 : DumpList(const nsAString& aKey, nsCounterList* aList, void* aClosure)
     333                 : {
     334               0 :     printf("Counter named \"%s\":\n", NS_ConvertUTF16toUTF8(aKey).get());
     335               0 :     nsCounterNode *node = aList->First();
     336                 : 
     337               0 :     if (node) {
     338               0 :         PRInt32 i = 0;
     339               0 :         do {
     340               0 :             const char *types[] = { "RESET", "INCREMENT", "USE" };
     341                 :             printf("  Node #%d @%p frame=%p index=%d type=%s valAfter=%d\n"
     342                 :                    "       scope-start=%p scope-prev=%p",
     343                 :                    i++, (void*)node, (void*)node->mPseudoFrame,
     344                 :                    node->mContentIndex, types[node->mType], node->mValueAfter,
     345               0 :                    (void*)node->mScopeStart, (void*)node->mScopePrev);
     346               0 :             if (node->mType == nsCounterNode::USE) {
     347               0 :                 nsAutoString text;
     348               0 :                 node->UseNode()->GetText(text);
     349               0 :                 printf(" text=%s", NS_ConvertUTF16toUTF8(text).get());
     350                 :             }
     351               0 :             printf("\n");
     352               0 :         } while ((node = aList->Next(node)) != aList->First());
     353                 :     }
     354               0 :     return PL_DHASH_NEXT;
     355                 : }
     356                 : 
     357                 : void
     358               0 : nsCounterManager::Dump()
     359                 : {
     360               0 :     printf("\n\nCounter Manager Lists:\n");
     361               0 :     mNames.EnumerateRead(DumpList, nsnull);
     362               0 :     printf("\n\n");
     363               0 : }
     364                 : #endif

Generated by: LCOV version 1.7