layout/base/nsCounterManager.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     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/. */
     7 /* implementation of CSS counters (for numbering things) */
     9 #include "nsCounterManager.h"
    10 #include "nsBulletFrame.h" // legacy location for list style type to text code
    11 #include "nsContentUtils.h"
    12 #include "nsTArray.h"
    13 #include "mozilla/Likely.h"
    14 #include "nsIContent.h"
    16 bool
    17 nsCounterUseNode::InitTextFrame(nsGenConList* aList,
    18         nsIFrame* aPseudoFrame, nsIFrame* aTextFrame)
    19 {
    20   nsCounterNode::InitTextFrame(aList, aPseudoFrame, aTextFrame);
    22   nsCounterList *counterList = static_cast<nsCounterList*>(aList);
    23   counterList->Insert(this);
    24   bool dirty = counterList->IsDirty();
    25   if (!dirty) {
    26     if (counterList->IsLast(this)) {
    27       Calc(counterList);
    28       nsAutoString contentString;
    29       GetText(contentString);
    30       aTextFrame->GetContent()->SetText(contentString, false);
    31     } else {
    32       // In all other cases (list already dirty or node not at the end),
    33       // just start with an empty string for now and when we recalculate
    34       // the list we'll change the value to the right one.
    35       counterList->SetDirty();
    36       return true;
    37     }
    38   }
    40   return false;
    41 }
    43 // assign the correct |mValueAfter| value to a node that has been inserted
    44 // Should be called immediately after calling |Insert|.
    45 void nsCounterUseNode::Calc(nsCounterList *aList)
    46 {
    47     NS_ASSERTION(!aList->IsDirty(),
    48                  "Why are we calculating with a dirty list?");
    49     mValueAfter = aList->ValueBefore(this);
    50 }
    52 // assign the correct |mValueAfter| value to a node that has been inserted
    53 // Should be called immediately after calling |Insert|.
    54 void nsCounterChangeNode::Calc(nsCounterList *aList)
    55 {
    56     NS_ASSERTION(!aList->IsDirty(),
    57                  "Why are we calculating with a dirty list?");
    58     if (mType == RESET) {
    59         mValueAfter = mChangeValue;
    60     } else {
    61         NS_ASSERTION(mType == INCREMENT, "invalid type");
    62         mValueAfter = nsCounterManager::IncrementCounter(aList->ValueBefore(this),
    63                                                          mChangeValue);
    64     }
    65 }
    67 // The text that should be displayed for this counter.
    68 void
    69 nsCounterUseNode::GetText(nsString& aResult)
    70 {
    71     aResult.Truncate();
    73     nsAutoTArray<nsCounterNode*, 8> stack;
    74     stack.AppendElement(static_cast<nsCounterNode*>(this));
    76     if (mAllCounters && mScopeStart)
    77         for (nsCounterNode *n = mScopeStart; n->mScopePrev; n = n->mScopeStart)
    78             stack.AppendElement(n->mScopePrev);
    80     const nsCSSValue& styleItem = mCounterStyle->Item(mAllCounters ? 2 : 1);
    81     int32_t style = styleItem.GetIntValue();
    82     const char16_t* separator;
    83     if (mAllCounters)
    84         separator = mCounterStyle->Item(1).GetStringBufferValue();
    86     for (uint32_t i = stack.Length() - 1;; --i) {
    87         nsCounterNode *n = stack[i];
    88         bool isTextRTL;
    89         nsBulletFrame::AppendCounterText(
    90                 style, n->mValueAfter, aResult, isTextRTL);
    91         if (i == 0)
    92             break;
    93         NS_ASSERTION(mAllCounters, "yikes, separator is uninitialized");
    94         aResult.Append(separator);
    95     }
    96 }
    98 void
    99 nsCounterList::SetScope(nsCounterNode *aNode)
   100 {
   101     // This function is responsible for setting |mScopeStart| and
   102     // |mScopePrev| (whose purpose is described in nsCounterManager.h).
   103     // We do this by starting from the node immediately preceding
   104     // |aNode| in content tree order, which is reasonably likely to be
   105     // the previous element in our scope (or, for a reset, the previous
   106     // element in the containing scope, which is what we want).  If
   107     // we're not in the same scope that it is, then it's too deep in the
   108     // frame tree, so we walk up parent scopes until we find something
   109     // appropriate.
   111     if (aNode == First()) {
   112         aNode->mScopeStart = nullptr;
   113         aNode->mScopePrev = nullptr;
   114         return;
   115     }
   117     // Get the content node for aNode's rendering object's *parent*,
   118     // since scope includes siblings, so we want a descendant check on
   119     // parents.
   120     nsIContent *nodeContent = aNode->mPseudoFrame->GetContent()->GetParent();
   122     for (nsCounterNode *prev = Prev(aNode), *start;
   123          prev; prev = start->mScopePrev) {
   124         // If |prev| starts a scope (because it's a real or implied
   125         // reset), we want it as the scope start rather than the start
   126         // of its enclosing scope.  Otherwise, there's no enclosing
   127         // scope, so the next thing in prev's scope shares its scope
   128         // start.
   129         start = (prev->mType == nsCounterNode::RESET || !prev->mScopeStart)
   130                   ? prev : prev->mScopeStart;
   132         // |startContent| is analogous to |nodeContent| (see above).
   133         nsIContent *startContent = start->mPseudoFrame->GetContent()->GetParent();
   134         NS_ASSERTION(nodeContent || !startContent,
   135                      "null check on startContent should be sufficient to "
   136                      "null check nodeContent as well, since if nodeContent "
   137                      "is for the root, startContent (which is before it) "
   138                      "must be too");
   140              // A reset's outer scope can't be a scope created by a sibling.
   141         if (!(aNode->mType == nsCounterNode::RESET &&
   142               nodeContent == startContent) &&
   143               // everything is inside the root (except the case above,
   144               // a second reset on the root)
   145             (!startContent ||
   146              nsContentUtils::ContentIsDescendantOf(nodeContent,
   147                                                    startContent))) {
   148             aNode->mScopeStart = start;
   149             aNode->mScopePrev  = prev;
   150             return;
   151         }
   152     }
   154     aNode->mScopeStart = nullptr;
   155     aNode->mScopePrev  = nullptr;
   156 }
   158 void
   159 nsCounterList::RecalcAll()
   160 {
   161     mDirty = false;
   163     nsCounterNode *node = First();
   164     if (!node)
   165         return;
   167     do {
   168         SetScope(node);
   169         node->Calc(this);
   171         if (node->mType == nsCounterNode::USE) {
   172             nsCounterUseNode *useNode = node->UseNode();
   173             // Null-check mText, since if the frame constructor isn't
   174             // batching, we could end up here while the node is being
   175             // constructed.
   176             if (useNode->mText) {
   177                 nsAutoString text;
   178                 useNode->GetText(text);
   179                 useNode->mText->SetData(text);
   180             }
   181         }
   182     } while ((node = Next(node)) != First());
   183 }
   185 nsCounterManager::nsCounterManager()
   186     : mNames(16)
   187 {
   188 }
   190 bool
   191 nsCounterManager::AddCounterResetsAndIncrements(nsIFrame *aFrame)
   192 {
   193     const nsStyleContent *styleContent = aFrame->StyleContent();
   194     if (!styleContent->CounterIncrementCount() &&
   195         !styleContent->CounterResetCount())
   196         return false;
   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     bool dirty = false;
   202     for (i = 0, i_end = styleContent->CounterResetCount(); i != i_end; ++i)
   203         dirty |= AddResetOrIncrement(aFrame, i,
   204                                      styleContent->GetCounterResetAt(i),
   205                                      nsCounterChangeNode::RESET);
   206     for (i = 0, i_end = styleContent->CounterIncrementCount(); i != i_end; ++i)
   207         dirty |= AddResetOrIncrement(aFrame, i,
   208                                      styleContent->GetCounterIncrementAt(i),
   209                                      nsCounterChangeNode::INCREMENT);
   210     return dirty;
   211 }
   213 bool
   214 nsCounterManager::AddResetOrIncrement(nsIFrame *aFrame, int32_t aIndex,
   215                                       const nsStyleCounterData *aCounterData,
   216                                       nsCounterNode::Type aType)
   217 {
   218     nsCounterChangeNode *node =
   219         new nsCounterChangeNode(aFrame, aType, aCounterData->mValue, aIndex);
   221     nsCounterList *counterList = CounterListFor(aCounterData->mCounter);
   222     if (!counterList) {
   223         NS_NOTREACHED("CounterListFor failed (should only happen on OOM)");
   224         return false;
   225     }
   227     counterList->Insert(node);
   228     if (!counterList->IsLast(node)) {
   229         // Tell the caller it's responsible for recalculating the entire
   230         // list.
   231         counterList->SetDirty();
   232         return true;
   233     }
   235     // Don't call Calc() if the list is already dirty -- it'll be recalculated
   236     // anyway, and trying to calculate with a dirty list doesn't work.
   237     if (MOZ_LIKELY(!counterList->IsDirty())) {
   238         node->Calc(counterList);
   239     }
   240     return false;
   241 }
   243 nsCounterList*
   244 nsCounterManager::CounterListFor(const nsSubstring& aCounterName)
   245 {
   246     // XXX Why doesn't nsTHashtable provide an API that allows us to use
   247     // get/put in one hashtable lookup?
   248     nsCounterList *counterList;
   249     if (!mNames.Get(aCounterName, &counterList)) {
   250         counterList = new nsCounterList();
   251         mNames.Put(aCounterName, counterList);
   252     }
   253     return counterList;
   254 }
   256 static PLDHashOperator
   257 RecalcDirtyLists(const nsAString& aKey, nsCounterList* aList, void* aClosure)
   258 {
   259     if (aList->IsDirty())
   260         aList->RecalcAll();
   261     return PL_DHASH_NEXT;
   262 }
   264 void
   265 nsCounterManager::RecalcAll()
   266 {
   267     mNames.EnumerateRead(RecalcDirtyLists, nullptr);
   268 }
   270 struct DestroyNodesData {
   271     DestroyNodesData(nsIFrame *aFrame)
   272         : mFrame(aFrame)
   273         , mDestroyedAny(false)
   274     {
   275     }
   277     nsIFrame *mFrame;
   278     bool mDestroyedAny;
   279 };
   281 static PLDHashOperator
   282 DestroyNodesInList(const nsAString& aKey, nsCounterList* aList, void* aClosure)
   283 {
   284     DestroyNodesData *data = static_cast<DestroyNodesData*>(aClosure);
   285     if (aList->DestroyNodesFor(data->mFrame)) {
   286         data->mDestroyedAny = true;
   287         aList->SetDirty();
   288     }
   289     return PL_DHASH_NEXT;
   290 }
   292 bool
   293 nsCounterManager::DestroyNodesFor(nsIFrame *aFrame)
   294 {
   295     DestroyNodesData data(aFrame);
   296     mNames.EnumerateRead(DestroyNodesInList, &data);
   297     return data.mDestroyedAny;
   298 }
   300 #ifdef DEBUG
   301 static PLDHashOperator
   302 DumpList(const nsAString& aKey, nsCounterList* aList, void* aClosure)
   303 {
   304     printf("Counter named \"%s\":\n", NS_ConvertUTF16toUTF8(aKey).get());
   305     nsCounterNode *node = aList->First();
   307     if (node) {
   308         int32_t i = 0;
   309         do {
   310             const char *types[] = { "RESET", "INCREMENT", "USE" };
   311             printf("  Node #%d @%p frame=%p index=%d type=%s valAfter=%d\n"
   312                    "       scope-start=%p scope-prev=%p",
   313                    i++, (void*)node, (void*)node->mPseudoFrame,
   314                    node->mContentIndex, types[node->mType], node->mValueAfter,
   315                    (void*)node->mScopeStart, (void*)node->mScopePrev);
   316             if (node->mType == nsCounterNode::USE) {
   317                 nsAutoString text;
   318                 node->UseNode()->GetText(text);
   319                 printf(" text=%s", NS_ConvertUTF16toUTF8(text).get());
   320             }
   321             printf("\n");
   322         } while ((node = aList->Next(node)) != aList->First());
   323     }
   324     return PL_DHASH_NEXT;
   325 }
   327 void
   328 nsCounterManager::Dump()
   329 {
   330     printf("\n\nCounter Manager Lists:\n");
   331     mNames.EnumerateRead(DumpList, nullptr);
   332     printf("\n\n");
   333 }
   334 #endif

mercurial