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 +