layout/base/RestyleTracker.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/base/RestyleTracker.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,280 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +/**
    1.10 + * A class which manages pending restyles.  This handles keeping track
    1.11 + * of what nodes restyles need to happen on and so forth.
    1.12 + */
    1.13 +
    1.14 +#include "RestyleTracker.h"
    1.15 +#include "nsStyleChangeList.h"
    1.16 +#include "RestyleManager.h"
    1.17 +#include "GeckoProfiler.h"
    1.18 +
    1.19 +namespace mozilla {
    1.20 +
    1.21 +inline nsIDocument*
    1.22 +RestyleTracker::Document() const {
    1.23 +  return mRestyleManager->PresContext()->Document();
    1.24 +}
    1.25 +
    1.26 +#define RESTYLE_ARRAY_STACKSIZE 128
    1.27 +
    1.28 +struct LaterSiblingCollector {
    1.29 +  RestyleTracker* tracker;
    1.30 +  nsTArray< nsRefPtr<dom::Element> >* elements;
    1.31 +};
    1.32 +
    1.33 +static PLDHashOperator
    1.34 +CollectLaterSiblings(nsISupports* aElement,
    1.35 +                     RestyleTracker::RestyleData& aData,
    1.36 +                     void* aSiblingCollector)
    1.37 +{
    1.38 +  dom::Element* element =
    1.39 +    static_cast<dom::Element*>(aElement);
    1.40 +  LaterSiblingCollector* collector =
    1.41 +    static_cast<LaterSiblingCollector*>(aSiblingCollector);
    1.42 +  // Only collect the entries that actually need restyling by us (and
    1.43 +  // haven't, for example, already been restyled).
    1.44 +  // It's important to not mess with the flags on entries not in our
    1.45 +  // document.
    1.46 +  if (element->GetCurrentDoc() == collector->tracker->Document() &&
    1.47 +      element->HasFlag(collector->tracker->RestyleBit()) &&
    1.48 +      (aData.mRestyleHint & eRestyle_LaterSiblings)) {
    1.49 +    collector->elements->AppendElement(element);
    1.50 +  }
    1.51 +
    1.52 +  return PL_DHASH_NEXT;
    1.53 +}
    1.54 +
    1.55 +struct RestyleCollector {
    1.56 +  RestyleTracker* tracker;
    1.57 +  RestyleTracker::RestyleEnumerateData** restyleArrayPtr;
    1.58 +};
    1.59 +
    1.60 +static PLDHashOperator
    1.61 +CollectRestyles(nsISupports* aElement,
    1.62 +                RestyleTracker::RestyleData& aData,
    1.63 +                void* aRestyleCollector)
    1.64 +{
    1.65 +  dom::Element* element =
    1.66 +    static_cast<dom::Element*>(aElement);
    1.67 +  RestyleCollector* collector =
    1.68 +    static_cast<RestyleCollector*>(aRestyleCollector);
    1.69 +  // Only collect the entries that actually need restyling by us (and
    1.70 +  // haven't, for example, already been restyled).
    1.71 +  // It's important to not mess with the flags on entries not in our
    1.72 +  // document.
    1.73 +  if (element->GetCurrentDoc() != collector->tracker->Document() ||
    1.74 +      !element->HasFlag(collector->tracker->RestyleBit())) {
    1.75 +    return PL_DHASH_NEXT;
    1.76 +  }
    1.77 +
    1.78 +  NS_ASSERTION(!element->HasFlag(collector->tracker->RootBit()) ||
    1.79 +               // Maybe we're just not reachable via the frame tree?
    1.80 +               (element->GetFlattenedTreeParent() &&
    1.81 +                (!element->GetFlattenedTreeParent()->GetPrimaryFrame()||
    1.82 +                 element->GetFlattenedTreeParent()->GetPrimaryFrame()->IsLeaf())) ||
    1.83 +               // Or not reachable due to an async reinsert we have
    1.84 +               // pending?  If so, we'll have a reframe hint around.
    1.85 +               // That incidentally makes it safe that we still have
    1.86 +               // the bit, since any descendants that didn't get added
    1.87 +               // to the roots list because we had the bits will be
    1.88 +               // completely restyled in a moment.
    1.89 +               (aData.mChangeHint & nsChangeHint_ReconstructFrame),
    1.90 +               "Why did this not get handled while processing mRestyleRoots?");
    1.91 +
    1.92 +  // Unset the restyle bits now, so if they get readded later as we
    1.93 +  // process we won't clobber that adding of the bit.
    1.94 +  element->UnsetFlags(collector->tracker->RestyleBit() |
    1.95 +                      collector->tracker->RootBit());
    1.96 +
    1.97 +  RestyleTracker::RestyleEnumerateData** restyleArrayPtr =
    1.98 +    collector->restyleArrayPtr;
    1.99 +  RestyleTracker::RestyleEnumerateData* currentRestyle =
   1.100 +    *restyleArrayPtr;
   1.101 +  currentRestyle->mElement = element;
   1.102 +  currentRestyle->mRestyleHint = aData.mRestyleHint;
   1.103 +  currentRestyle->mChangeHint = aData.mChangeHint;
   1.104 +
   1.105 +  // Increment to the next slot in the array
   1.106 +  *restyleArrayPtr = currentRestyle + 1;
   1.107 +
   1.108 +  return PL_DHASH_NEXT;
   1.109 +}
   1.110 +
   1.111 +inline void
   1.112 +RestyleTracker::ProcessOneRestyle(Element* aElement,
   1.113 +                                  nsRestyleHint aRestyleHint,
   1.114 +                                  nsChangeHint aChangeHint)
   1.115 +{
   1.116 +  NS_PRECONDITION((aRestyleHint & eRestyle_LaterSiblings) == 0,
   1.117 +                  "Someone should have handled this before calling us");
   1.118 +  NS_PRECONDITION(Document(), "Must have a document");
   1.119 +  NS_PRECONDITION(aElement->GetCurrentDoc() == Document(),
   1.120 +                  "Element has unexpected document");
   1.121 +
   1.122 +  nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
   1.123 +  if (aRestyleHint & (eRestyle_Self | eRestyle_Subtree)) {
   1.124 +    mRestyleManager->RestyleElement(aElement, primaryFrame, aChangeHint,
   1.125 +                                    *this,
   1.126 +                                    (aRestyleHint & eRestyle_Subtree) != 0);
   1.127 +  } else if (aChangeHint &&
   1.128 +             (primaryFrame ||
   1.129 +              (aChangeHint & nsChangeHint_ReconstructFrame))) {
   1.130 +    // Don't need to recompute style; just apply the hint
   1.131 +    nsStyleChangeList changeList;
   1.132 +    changeList.AppendChange(primaryFrame, aElement, aChangeHint);
   1.133 +    mRestyleManager->ProcessRestyledFrames(changeList);
   1.134 +  }
   1.135 +}
   1.136 +
   1.137 +void
   1.138 +RestyleTracker::DoProcessRestyles()
   1.139 +{
   1.140 +  PROFILER_LABEL("CSS", "ProcessRestyles");
   1.141 +
   1.142 +  mRestyleManager->BeginProcessingRestyles();
   1.143 +
   1.144 +  // loop so that we process any restyle events generated by processing
   1.145 +  while (mPendingRestyles.Count()) {
   1.146 +    if (mHaveLaterSiblingRestyles) {
   1.147 +      // Convert them to individual restyles on all the later siblings
   1.148 +      nsAutoTArray<nsRefPtr<Element>, RESTYLE_ARRAY_STACKSIZE> laterSiblingArr;
   1.149 +      LaterSiblingCollector siblingCollector = { this, &laterSiblingArr };
   1.150 +      mPendingRestyles.Enumerate(CollectLaterSiblings, &siblingCollector);
   1.151 +      for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
   1.152 +        Element* element = laterSiblingArr[i];
   1.153 +        for (nsIContent* sibling = element->GetNextSibling();
   1.154 +             sibling;
   1.155 +             sibling = sibling->GetNextSibling()) {
   1.156 +          if (sibling->IsElement() &&
   1.157 +              AddPendingRestyle(sibling->AsElement(), eRestyle_Subtree,
   1.158 +                                NS_STYLE_HINT_NONE)) {
   1.159 +              // Nothing else to do here; we'll handle the following
   1.160 +              // siblings when we get to |sibling| in laterSiblingArr.
   1.161 +            break;
   1.162 +          }
   1.163 +        }
   1.164 +      }
   1.165 +
   1.166 +      // Now remove all those eRestyle_LaterSiblings bits
   1.167 +      for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
   1.168 +        Element* element = laterSiblingArr[i];
   1.169 +        NS_ASSERTION(element->HasFlag(RestyleBit()), "How did that happen?");
   1.170 +        RestyleData data;
   1.171 +#ifdef DEBUG
   1.172 +        bool found =
   1.173 +#endif
   1.174 +          mPendingRestyles.Get(element, &data);
   1.175 +        NS_ASSERTION(found, "Where did our entry go?");
   1.176 +        data.mRestyleHint =
   1.177 +          nsRestyleHint(data.mRestyleHint & ~eRestyle_LaterSiblings);
   1.178 +
   1.179 +        mPendingRestyles.Put(element, data);
   1.180 +      }
   1.181 +
   1.182 +      mHaveLaterSiblingRestyles = false;
   1.183 +    }
   1.184 +
   1.185 +    uint32_t rootCount;
   1.186 +    while ((rootCount = mRestyleRoots.Length())) {
   1.187 +      // Make sure to pop the element off our restyle root array, so
   1.188 +      // that we can freely append to the array as we process this
   1.189 +      // element.
   1.190 +      nsRefPtr<Element> element;
   1.191 +      element.swap(mRestyleRoots[rootCount - 1]);
   1.192 +      mRestyleRoots.RemoveElementAt(rootCount - 1);
   1.193 +
   1.194 +      // Do the document check before calling GetRestyleData, since we
   1.195 +      // don't want to do the sibling-processing GetRestyleData does if
   1.196 +      // the node is no longer relevant.
   1.197 +      if (element->GetCurrentDoc() != Document()) {
   1.198 +        // Content node has been removed from our document; nothing else
   1.199 +        // to do here
   1.200 +        continue;
   1.201 +      }
   1.202 +
   1.203 +      RestyleData data;
   1.204 +      if (!GetRestyleData(element, &data)) {
   1.205 +        continue;
   1.206 +      }
   1.207 +
   1.208 +      ProcessOneRestyle(element, data.mRestyleHint, data.mChangeHint);
   1.209 +    }
   1.210 +
   1.211 +    if (mHaveLaterSiblingRestyles) {
   1.212 +      // Keep processing restyles for now
   1.213 +      continue;
   1.214 +    }
   1.215 +
   1.216 +    // Now we only have entries with change hints left.  To be safe in
   1.217 +    // case of reentry from the handing of the change hint, use a
   1.218 +    // scratch array instead of calling out to ProcessOneRestyle while
   1.219 +    // enumerating the hashtable.  Use the stack if we can, otherwise
   1.220 +    // fall back on heap-allocation.
   1.221 +    nsAutoTArray<RestyleEnumerateData, RESTYLE_ARRAY_STACKSIZE> restyleArr;
   1.222 +    RestyleEnumerateData* restylesToProcess =
   1.223 +      restyleArr.AppendElements(mPendingRestyles.Count());
   1.224 +    if (restylesToProcess) {
   1.225 +      RestyleEnumerateData* lastRestyle = restylesToProcess;
   1.226 +      RestyleCollector collector = { this, &lastRestyle };
   1.227 +      mPendingRestyles.Enumerate(CollectRestyles, &collector);
   1.228 +
   1.229 +      // Clear the hashtable now that we don't need it anymore
   1.230 +      mPendingRestyles.Clear();
   1.231 +
   1.232 +      for (RestyleEnumerateData* currentRestyle = restylesToProcess;
   1.233 +           currentRestyle != lastRestyle;
   1.234 +           ++currentRestyle) {
   1.235 +        ProcessOneRestyle(currentRestyle->mElement,
   1.236 +                          currentRestyle->mRestyleHint,
   1.237 +                          currentRestyle->mChangeHint);
   1.238 +      }
   1.239 +    }
   1.240 +  }
   1.241 +
   1.242 +  mRestyleManager->EndProcessingRestyles();
   1.243 +}
   1.244 +
   1.245 +bool
   1.246 +RestyleTracker::GetRestyleData(Element* aElement, RestyleData* aData)
   1.247 +{
   1.248 +  NS_PRECONDITION(aElement->GetCurrentDoc() == Document(),
   1.249 +                  "Unexpected document; this will lead to incorrect behavior!");
   1.250 +
   1.251 +  if (!aElement->HasFlag(RestyleBit())) {
   1.252 +    NS_ASSERTION(!aElement->HasFlag(RootBit()), "Bogus root bit?");
   1.253 +    return false;
   1.254 +  }
   1.255 +
   1.256 +#ifdef DEBUG
   1.257 +  bool gotData =
   1.258 +#endif
   1.259 +  mPendingRestyles.Get(aElement, aData);
   1.260 +  NS_ASSERTION(gotData, "Must have data if restyle bit is set");
   1.261 +
   1.262 +  if (aData->mRestyleHint & eRestyle_LaterSiblings) {
   1.263 +    // Someone readded the eRestyle_LaterSiblings hint for this
   1.264 +    // element.  Leave it around for now, but remove the other restyle
   1.265 +    // hints and the change hint for it.  Also unset its root bit,
   1.266 +    // since it's no longer a root with the new restyle data.
   1.267 +    RestyleData newData;
   1.268 +    newData.mChangeHint = nsChangeHint(0);
   1.269 +    newData.mRestyleHint = eRestyle_LaterSiblings;
   1.270 +    mPendingRestyles.Put(aElement, newData);
   1.271 +    aElement->UnsetFlags(RootBit());
   1.272 +    aData->mRestyleHint =
   1.273 +      nsRestyleHint(aData->mRestyleHint & ~eRestyle_LaterSiblings);
   1.274 +  } else {
   1.275 +    mPendingRestyles.Remove(aElement);
   1.276 +    aElement->UnsetFlags(mRestyleBits);
   1.277 +  }
   1.278 +
   1.279 +  return true;
   1.280 +}
   1.281 +
   1.282 +} // namespace mozilla
   1.283 +

mercurial