Bug 846320 - Remove SimpleTest.expectAssertions in test_seek.html. r=dbaron
[gecko.git] / layout / base / RestyleTracker.cpp
bloba0a86d2e6a303615388c4454fcd3bf585b4c3a37
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/. */
6 /**
7 * A class which manages pending restyles. This handles keeping track
8 * of what nodes restyles need to happen on and so forth.
9 */
11 #include "RestyleTracker.h"
12 #include "nsCSSFrameConstructor.h"
13 #include "nsStyleChangeList.h"
14 #include "sampler.h"
16 namespace mozilla {
17 namespace css {
19 inline nsIDocument*
20 RestyleTracker::Document() const {
21 return mFrameConstructor->mDocument;
24 #define RESTYLE_ARRAY_STACKSIZE 128
26 struct LaterSiblingCollector {
27 RestyleTracker* tracker;
28 nsTArray< nsRefPtr<dom::Element> >* elements;
31 static PLDHashOperator
32 CollectLaterSiblings(nsISupports* aElement,
33 RestyleTracker::RestyleData& aData,
34 void* aSiblingCollector)
36 dom::Element* element =
37 static_cast<dom::Element*>(aElement);
38 LaterSiblingCollector* collector =
39 static_cast<LaterSiblingCollector*>(aSiblingCollector);
40 // Only collect the entries that actually need restyling by us (and
41 // haven't, for example, already been restyled).
42 // It's important to not mess with the flags on entries not in our
43 // document.
44 if (element->GetCurrentDoc() == collector->tracker->Document() &&
45 element->HasFlag(collector->tracker->RestyleBit()) &&
46 (aData.mRestyleHint & eRestyle_LaterSiblings)) {
47 collector->elements->AppendElement(element);
50 return PL_DHASH_NEXT;
53 struct RestyleCollector {
54 RestyleTracker* tracker;
55 RestyleTracker::RestyleEnumerateData** restyleArrayPtr;
58 static PLDHashOperator
59 CollectRestyles(nsISupports* aElement,
60 RestyleTracker::RestyleData& aData,
61 void* aRestyleCollector)
63 dom::Element* element =
64 static_cast<dom::Element*>(aElement);
65 RestyleCollector* collector =
66 static_cast<RestyleCollector*>(aRestyleCollector);
67 // Only collect the entries that actually need restyling by us (and
68 // haven't, for example, already been restyled).
69 // It's important to not mess with the flags on entries not in our
70 // document.
71 if (element->GetCurrentDoc() != collector->tracker->Document() ||
72 !element->HasFlag(collector->tracker->RestyleBit())) {
73 return PL_DHASH_NEXT;
76 NS_ASSERTION(!element->HasFlag(collector->tracker->RootBit()) ||
77 // Maybe we're just not reachable via the frame tree?
78 (element->GetFlattenedTreeParent() &&
79 (!element->GetFlattenedTreeParent()->GetPrimaryFrame()||
80 element->GetFlattenedTreeParent()->GetPrimaryFrame()->IsLeaf())) ||
81 // Or not reachable due to an async reinsert we have
82 // pending? If so, we'll have a reframe hint around.
83 // That incidentally makes it safe that we still have
84 // the bit, since any descendants that didn't get added
85 // to the roots list because we had the bits will be
86 // completely restyled in a moment.
87 (aData.mChangeHint & nsChangeHint_ReconstructFrame),
88 "Why did this not get handled while processing mRestyleRoots?");
90 // Unset the restyle bits now, so if they get readded later as we
91 // process we won't clobber that adding of the bit.
92 element->UnsetFlags(collector->tracker->RestyleBit() |
93 collector->tracker->RootBit());
95 RestyleTracker::RestyleEnumerateData** restyleArrayPtr =
96 collector->restyleArrayPtr;
97 RestyleTracker::RestyleEnumerateData* currentRestyle =
98 *restyleArrayPtr;
99 currentRestyle->mElement = element;
100 currentRestyle->mRestyleHint = aData.mRestyleHint;
101 currentRestyle->mChangeHint = aData.mChangeHint;
103 // Increment to the next slot in the array
104 *restyleArrayPtr = currentRestyle + 1;
106 return PL_DHASH_NEXT;
109 inline void
110 RestyleTracker::ProcessOneRestyle(Element* aElement,
111 nsRestyleHint aRestyleHint,
112 nsChangeHint aChangeHint)
114 NS_PRECONDITION((aRestyleHint & eRestyle_LaterSiblings) == 0,
115 "Someone should have handled this before calling us");
116 NS_PRECONDITION(Document(), "Must have a document");
117 NS_PRECONDITION(aElement->GetCurrentDoc() == Document(),
118 "Element has unexpected document");
120 nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
121 if (aRestyleHint & (eRestyle_Self | eRestyle_Subtree)) {
122 mFrameConstructor->RestyleElement(aElement, primaryFrame, aChangeHint,
123 *this,
124 (aRestyleHint & eRestyle_Subtree) != 0);
125 } else if (aChangeHint &&
126 (primaryFrame ||
127 (aChangeHint & nsChangeHint_ReconstructFrame))) {
128 // Don't need to recompute style; just apply the hint
129 nsStyleChangeList changeList;
130 changeList.AppendChange(primaryFrame, aElement, aChangeHint);
131 mFrameConstructor->ProcessRestyledFrames(changeList);
135 void
136 RestyleTracker::DoProcessRestyles()
138 SAMPLE_LABEL("CSS", "ProcessRestyles");
139 // Make sure to not rebuild quote or counter lists while we're
140 // processing restyles
141 mFrameConstructor->BeginUpdate();
143 mFrameConstructor->mInStyleRefresh = true;
145 // loop so that we process any restyle events generated by processing
146 while (mPendingRestyles.Count()) {
147 if (mHaveLaterSiblingRestyles) {
148 // Convert them to individual restyles on all the later siblings
149 nsAutoTArray<nsRefPtr<Element>, RESTYLE_ARRAY_STACKSIZE> laterSiblingArr;
150 LaterSiblingCollector siblingCollector = { this, &laterSiblingArr };
151 mPendingRestyles.Enumerate(CollectLaterSiblings, &siblingCollector);
152 for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
153 Element* element = laterSiblingArr[i];
154 for (nsIContent* sibling = element->GetNextSibling();
155 sibling;
156 sibling = sibling->GetNextSibling()) {
157 if (sibling->IsElement() &&
158 AddPendingRestyle(sibling->AsElement(), eRestyle_Subtree,
159 NS_STYLE_HINT_NONE)) {
160 // Nothing else to do here; we'll handle the following
161 // siblings when we get to |sibling| in laterSiblingArr.
162 break;
167 // Now remove all those eRestyle_LaterSiblings bits
168 for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
169 Element* element = laterSiblingArr[i];
170 NS_ASSERTION(element->HasFlag(RestyleBit()), "How did that happen?");
171 RestyleData data;
172 #ifdef DEBUG
173 bool found =
174 #endif
175 mPendingRestyles.Get(element, &data);
176 NS_ASSERTION(found, "Where did our entry go?");
177 data.mRestyleHint =
178 nsRestyleHint(data.mRestyleHint & ~eRestyle_LaterSiblings);
180 mPendingRestyles.Put(element, data);
183 mHaveLaterSiblingRestyles = false;
186 uint32_t rootCount;
187 while ((rootCount = mRestyleRoots.Length())) {
188 // Make sure to pop the element off our restyle root array, so
189 // that we can freely append to the array as we process this
190 // element.
191 nsRefPtr<Element> element;
192 element.swap(mRestyleRoots[rootCount - 1]);
193 mRestyleRoots.RemoveElementAt(rootCount - 1);
195 // Do the document check before calling GetRestyleData, since we
196 // don't want to do the sibling-processing GetRestyleData does if
197 // the node is no longer relevant.
198 if (element->GetCurrentDoc() != Document()) {
199 // Content node has been removed from our document; nothing else
200 // to do here
201 continue;
204 RestyleData data;
205 if (!GetRestyleData(element, &data)) {
206 continue;
209 ProcessOneRestyle(element, data.mRestyleHint, data.mChangeHint);
212 if (mHaveLaterSiblingRestyles) {
213 // Keep processing restyles for now
214 continue;
217 // Now we only have entries with change hints left. To be safe in
218 // case of reentry from the handing of the change hint, use a
219 // scratch array instead of calling out to ProcessOneRestyle while
220 // enumerating the hashtable. Use the stack if we can, otherwise
221 // fall back on heap-allocation.
222 nsAutoTArray<RestyleEnumerateData, RESTYLE_ARRAY_STACKSIZE> restyleArr;
223 RestyleEnumerateData* restylesToProcess =
224 restyleArr.AppendElements(mPendingRestyles.Count());
225 if (restylesToProcess) {
226 RestyleEnumerateData* lastRestyle = restylesToProcess;
227 RestyleCollector collector = { this, &lastRestyle };
228 mPendingRestyles.Enumerate(CollectRestyles, &collector);
230 // Clear the hashtable now that we don't need it anymore
231 mPendingRestyles.Clear();
233 for (RestyleEnumerateData* currentRestyle = restylesToProcess;
234 currentRestyle != lastRestyle;
235 ++currentRestyle) {
236 ProcessOneRestyle(currentRestyle->mElement,
237 currentRestyle->mRestyleHint,
238 currentRestyle->mChangeHint);
243 mFrameConstructor->FlushOverflowChangedTracker();
245 // Set mInStyleRefresh to false now, since the EndUpdate call might
246 // add more restyles.
247 mFrameConstructor->mInStyleRefresh = false;
249 mFrameConstructor->EndUpdate();
251 #ifdef DEBUG
252 mFrameConstructor->mPresShell->VerifyStyleTree();
253 #endif
256 bool
257 RestyleTracker::GetRestyleData(Element* aElement, RestyleData* aData)
259 NS_PRECONDITION(aElement->GetCurrentDoc() == Document(),
260 "Unexpected document; this will lead to incorrect behavior!");
262 if (!aElement->HasFlag(RestyleBit())) {
263 NS_ASSERTION(!aElement->HasFlag(RootBit()), "Bogus root bit?");
264 return false;
267 #ifdef DEBUG
268 bool gotData =
269 #endif
270 mPendingRestyles.Get(aElement, aData);
271 NS_ASSERTION(gotData, "Must have data if restyle bit is set");
273 if (aData->mRestyleHint & eRestyle_LaterSiblings) {
274 // Someone readded the eRestyle_LaterSiblings hint for this
275 // element. Leave it around for now, but remove the other restyle
276 // hints and the change hint for it. Also unset its root bit,
277 // since it's no longer a root with the new restyle data.
278 RestyleData newData;
279 newData.mChangeHint = nsChangeHint(0);
280 newData.mRestyleHint = eRestyle_LaterSiblings;
281 mPendingRestyles.Put(aElement, newData);
282 aElement->UnsetFlags(RootBit());
283 aData->mRestyleHint =
284 nsRestyleHint(aData->mRestyleHint & ~eRestyle_LaterSiblings);
285 } else {
286 mPendingRestyles.Remove(aElement);
287 aElement->UnsetFlags(mRestyleBits);
290 return true;
293 } // namespace css
294 } // namespace mozilla