LCOV - code coverage report
Current view: top level - layout/base - nsCounterManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 6 145 4.1 %
Date: 2017-07-14 16:53:18 Functions: 1 14 7.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : /* implementation of CSS counters (for numbering things) */
       7             : 
       8             : #include "nsCounterManager.h"
       9             : 
      10             : #include "mozilla/Likely.h"
      11             : #include "mozilla/WritingModes.h"
      12             : #include "nsBulletFrame.h" // legacy location for list style type to text code
      13             : #include "nsContentUtils.h"
      14             : #include "nsIContent.h"
      15             : #include "nsTArray.h"
      16             : 
      17             : using namespace mozilla;
      18             : 
      19             : bool
      20           0 : nsCounterUseNode::InitTextFrame(nsGenConList* aList,
      21             :                                 nsIFrame* aPseudoFrame,
      22             :                                 nsIFrame* aTextFrame)
      23             : {
      24           0 :   nsCounterNode::InitTextFrame(aList, aPseudoFrame, aTextFrame);
      25             : 
      26           0 :   nsCounterList* counterList = static_cast<nsCounterList*>(aList);
      27           0 :   counterList->Insert(this);
      28           0 :   aPseudoFrame->AddStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE);
      29           0 :   bool dirty = counterList->IsDirty();
      30           0 :   if (!dirty) {
      31           0 :     if (counterList->IsLast(this)) {
      32           0 :       Calc(counterList);
      33           0 :       nsAutoString contentString;
      34           0 :       GetText(contentString);
      35           0 :       aTextFrame->GetContent()->SetText(contentString, false);
      36             :     } else {
      37             :       // In all other cases (list already dirty or node not at the end),
      38             :       // just start with an empty string for now and when we recalculate
      39             :       // the list we'll change the value to the right one.
      40           0 :       counterList->SetDirty();
      41           0 :       return true;
      42             :     }
      43             :   }
      44             : 
      45           0 :   return false;
      46             : }
      47             : 
      48             : // assign the correct |mValueAfter| value to a node that has been inserted
      49             : // Should be called immediately after calling |Insert|.
      50             : void
      51           0 : nsCounterUseNode::Calc(nsCounterList* aList)
      52             : {
      53           0 :   NS_ASSERTION(!aList->IsDirty(),
      54             :                "Why are we calculating with a dirty list?");
      55           0 :   mValueAfter = aList->ValueBefore(this);
      56           0 : }
      57             : 
      58             : // assign the correct |mValueAfter| value to a node that has been inserted
      59             : // Should be called immediately after calling |Insert|.
      60             : void
      61           0 : nsCounterChangeNode::Calc(nsCounterList* aList)
      62             : {
      63           0 :   NS_ASSERTION(!aList->IsDirty(), "Why are we calculating with a dirty list?");
      64           0 :   if (mType == RESET) {
      65           0 :     mValueAfter = mChangeValue;
      66             :   } else {
      67           0 :     NS_ASSERTION(mType == INCREMENT, "invalid type");
      68           0 :     mValueAfter = nsCounterManager::IncrementCounter(aList->ValueBefore(this),
      69             :                                                      mChangeValue);
      70             :   }
      71           0 : }
      72             : 
      73             : // The text that should be displayed for this counter.
      74             : void
      75           0 : nsCounterUseNode::GetText(nsString& aResult)
      76             : {
      77           0 :   aResult.Truncate();
      78             : 
      79           0 :   AutoTArray<nsCounterNode*, 8> stack;
      80           0 :   stack.AppendElement(static_cast<nsCounterNode*>(this));
      81             : 
      82           0 :   if (mAllCounters && mScopeStart) {
      83           0 :     for (nsCounterNode* n = mScopeStart; n->mScopePrev; n = n->mScopeStart) {
      84           0 :       stack.AppendElement(n->mScopePrev);
      85             :     }
      86             :   }
      87             : 
      88           0 :   WritingMode wm = mPseudoFrame ?
      89           0 :     mPseudoFrame->GetWritingMode() : WritingMode();
      90           0 :   for (uint32_t i = stack.Length() - 1;; --i) {
      91           0 :     nsCounterNode* n = stack[i];
      92           0 :     nsAutoString text;
      93             :     bool isTextRTL;
      94           0 :     mCounterStyle->GetCounterText(n->mValueAfter, wm, text, isTextRTL);
      95           0 :     aResult.Append(text);
      96           0 :     if (i == 0) {
      97           0 :       break;
      98             :     }
      99           0 :     aResult.Append(mSeparator);
     100           0 :   }
     101           0 : }
     102             : 
     103             : void
     104           0 : nsCounterList::SetScope(nsCounterNode* aNode)
     105             : {
     106             :   // This function is responsible for setting |mScopeStart| and
     107             :   // |mScopePrev| (whose purpose is described in nsCounterManager.h).
     108             :   // We do this by starting from the node immediately preceding
     109             :   // |aNode| in content tree order, which is reasonably likely to be
     110             :   // the previous element in our scope (or, for a reset, the previous
     111             :   // element in the containing scope, which is what we want).  If
     112             :   // we're not in the same scope that it is, then it's too deep in the
     113             :   // frame tree, so we walk up parent scopes until we find something
     114             :   // appropriate.
     115             : 
     116           0 :   if (aNode == First()) {
     117           0 :     aNode->mScopeStart = nullptr;
     118           0 :     aNode->mScopePrev = nullptr;
     119           0 :     return;
     120             :   }
     121             : 
     122             :   // Get the content node for aNode's rendering object's *parent*,
     123             :   // since scope includes siblings, so we want a descendant check on
     124             :   // parents.
     125           0 :   nsIContent* nodeContent = aNode->mPseudoFrame->GetContent()->GetParent();
     126             : 
     127           0 :   for (nsCounterNode* prev = Prev(aNode), *start;
     128           0 :        prev; prev = start->mScopePrev) {
     129             :     // If |prev| starts a scope (because it's a real or implied
     130             :     // reset), we want it as the scope start rather than the start
     131             :     // of its enclosing scope.  Otherwise, there's no enclosing
     132             :     // scope, so the next thing in prev's scope shares its scope
     133             :     // start.
     134           0 :     start = (prev->mType == nsCounterNode::RESET || !prev->mScopeStart)
     135           0 :       ? prev : prev->mScopeStart;
     136             : 
     137             :     // |startContent| is analogous to |nodeContent| (see above).
     138           0 :     nsIContent* startContent = start->mPseudoFrame->GetContent()->GetParent();
     139           0 :     NS_ASSERTION(nodeContent || !startContent,
     140             :                  "null check on startContent should be sufficient to "
     141             :                  "null check nodeContent as well, since if nodeContent "
     142             :                  "is for the root, startContent (which is before it) "
     143             :                  "must be too");
     144             : 
     145             :     // A reset's outer scope can't be a scope created by a sibling.
     146           0 :     if (!(aNode->mType == nsCounterNode::RESET &&
     147           0 :           nodeContent == startContent) &&
     148             :         // everything is inside the root (except the case above,
     149             :         // a second reset on the root)
     150           0 :         (!startContent ||
     151           0 :          nsContentUtils::ContentIsDescendantOf(nodeContent,
     152             :                                                startContent))) {
     153           0 :       aNode->mScopeStart = start;
     154           0 :       aNode->mScopePrev  = prev;
     155           0 :       return;
     156             :     }
     157             :   }
     158             : 
     159           0 :   aNode->mScopeStart = nullptr;
     160           0 :   aNode->mScopePrev  = nullptr;
     161             : }
     162             : 
     163             : void
     164           0 : nsCounterList::RecalcAll()
     165             : {
     166           0 :   mDirty = false;
     167             : 
     168           0 :   for (nsCounterNode* node = First(); node; node = Next(node)) {
     169           0 :     SetScope(node);
     170           0 :     node->Calc(this);
     171             : 
     172           0 :     if (node->mType == nsCounterNode::USE) {
     173           0 :       nsCounterUseNode* useNode = node->UseNode();
     174             :       // Null-check mText, since if the frame constructor isn't
     175             :       // batching, we could end up here while the node is being
     176             :       // constructed.
     177           0 :       if (useNode->mText) {
     178           0 :         nsAutoString text;
     179           0 :         useNode->GetText(text);
     180           0 :         useNode->mText->SetData(text);
     181             :       }
     182             :     }
     183             :   }
     184           0 : }
     185             : 
     186             : bool
     187         578 : nsCounterManager::AddCounterResetsAndIncrements(nsIFrame* aFrame)
     188             : {
     189         578 :   const nsStyleContent* styleContent = aFrame->StyleContent();
     190        1156 :   if (!styleContent->CounterIncrementCount() &&
     191         578 :       !styleContent->CounterResetCount()) {
     192         578 :     MOZ_ASSERT(!aFrame->HasAnyStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE));
     193         578 :     return false;
     194             :   }
     195             : 
     196           0 :   aFrame->AddStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE);
     197             : 
     198             :   // Add in order, resets first, so all the comparisons will be optimized
     199             :   // for addition at the end of the list.
     200             :   int32_t i, i_end;
     201           0 :   bool dirty = false;
     202           0 :   for (i = 0, i_end = styleContent->CounterResetCount(); i != i_end; ++i) {
     203           0 :     dirty |= AddResetOrIncrement(aFrame, i, styleContent->CounterResetAt(i),
     204             :                                  nsCounterChangeNode::RESET);
     205             :   }
     206           0 :   for (i = 0, i_end = styleContent->CounterIncrementCount(); i != i_end; ++i) {
     207           0 :     dirty |= AddResetOrIncrement(aFrame, i, styleContent->CounterIncrementAt(i),
     208             :                                  nsCounterChangeNode::INCREMENT);
     209             :   }
     210           0 :   return dirty;
     211             : }
     212             : 
     213             : bool
     214           0 : nsCounterManager::AddResetOrIncrement(nsIFrame* aFrame, int32_t aIndex,
     215             :                                       const nsStyleCounterData& aCounterData,
     216             :                                       nsCounterNode::Type aType)
     217             : {
     218             :   nsCounterChangeNode* node =
     219           0 :     new nsCounterChangeNode(aFrame, aType, aCounterData.mValue, aIndex);
     220             : 
     221           0 :   nsCounterList* counterList = CounterListFor(aCounterData.mCounter);
     222           0 :   counterList->Insert(node);
     223           0 :   if (!counterList->IsLast(node)) {
     224             :     // Tell the caller it's responsible for recalculating the entire
     225             :     // list.
     226           0 :     counterList->SetDirty();
     227           0 :     return true;
     228             :   }
     229             : 
     230             :   // Don't call Calc() if the list is already dirty -- it'll be recalculated
     231             :   // anyway, and trying to calculate with a dirty list doesn't work.
     232           0 :   if (MOZ_LIKELY(!counterList->IsDirty())) {
     233           0 :     node->Calc(counterList);
     234             :   }
     235           0 :   return false;
     236             : }
     237             : 
     238             : nsCounterList*
     239           0 : nsCounterManager::CounterListFor(const nsAString& aCounterName)
     240             : {
     241           0 :   return mNames.LookupForAdd(aCounterName).OrInsert([]() {
     242           0 :     return new nsCounterList();
     243           0 :   });
     244             : }
     245             : 
     246             : void
     247           0 : nsCounterManager::RecalcAll()
     248             : {
     249           0 :   for (auto iter = mNames.Iter(); !iter.Done(); iter.Next()) {
     250           0 :     nsCounterList* list = iter.UserData();
     251           0 :     if (list->IsDirty()) {
     252           0 :       list->RecalcAll();
     253             :     }
     254             :   }
     255           0 : }
     256             : 
     257             : void
     258           0 : nsCounterManager::SetAllDirty()
     259             : {
     260           0 :   for (auto iter = mNames.Iter(); !iter.Done(); iter.Next()) {
     261           0 :     iter.UserData()->SetDirty();
     262             :   }
     263           0 : }
     264             : 
     265             : bool
     266           0 : nsCounterManager::DestroyNodesFor(nsIFrame* aFrame)
     267             : {
     268           0 :   MOZ_ASSERT(aFrame->HasAnyStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE),
     269             :              "why call me?");
     270           0 :   bool destroyedAny = false;
     271           0 :   for (auto iter = mNames.Iter(); !iter.Done(); iter.Next()) {
     272           0 :     nsCounterList* list = iter.UserData();
     273           0 :     if (list->DestroyNodesFor(aFrame)) {
     274           0 :       destroyedAny = true;
     275           0 :       list->SetDirty();
     276             :     }
     277             :   }
     278           0 :   return destroyedAny;
     279             : }
     280             : 
     281             : #ifdef DEBUG
     282             : void
     283           0 : nsCounterManager::Dump()
     284             : {
     285           0 :   printf("\n\nCounter Manager Lists:\n");
     286           0 :   for (auto iter = mNames.Iter(); !iter.Done(); iter.Next()) {
     287           0 :     printf("Counter named \"%s\":\n",
     288           0 :            NS_ConvertUTF16toUTF8(iter.Key()).get());
     289             : 
     290           0 :     nsCounterList* list = iter.UserData();
     291           0 :     int32_t i = 0;
     292           0 :     for (nsCounterNode* node = list->First(); node; node = list->Next(node)) {
     293           0 :       const char* types[] = { "RESET", "INCREMENT", "USE" };
     294           0 :       printf("  Node #%d @%p frame=%p index=%d type=%s valAfter=%d\n"
     295             :              "       scope-start=%p scope-prev=%p",
     296           0 :              i++, (void*)node, (void*)node->mPseudoFrame,
     297           0 :              node->mContentIndex, types[node->mType],
     298           0 :              node->mValueAfter, (void*)node->mScopeStart,
     299           0 :              (void*)node->mScopePrev);
     300           0 :       if (node->mType == nsCounterNode::USE) {
     301           0 :         nsAutoString text;
     302           0 :         node->UseNode()->GetText(text);
     303           0 :         printf(" text=%s", NS_ConvertUTF16toUTF8(text).get());
     304             :       }
     305           0 :       printf("\n");
     306             :     }
     307             :   }
     308           0 :   printf("\n\n");
     309           0 : }
     310             : #endif

Generated by: LCOV version 1.13