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/. */
7 * A class which manages pending restyles. This handles keeping track
8 * of what nodes restyles need to happen on and so forth.
11 #include "RestyleTracker.h"
12 #include "nsCSSFrameConstructor.h"
13 #include "nsStyleChangeList.h"
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
44 if (element
->GetCurrentDoc() == collector
->tracker
->Document() &&
45 element
->HasFlag(collector
->tracker
->RestyleBit()) &&
46 (aData
.mRestyleHint
& eRestyle_LaterSiblings
)) {
47 collector
->elements
->AppendElement(element
);
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
71 if (element
->GetCurrentDoc() != collector
->tracker
->Document() ||
72 !element
->HasFlag(collector
->tracker
->RestyleBit())) {
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
=
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
;
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
,
124 (aRestyleHint
& eRestyle_Subtree
) != 0);
125 } else if (aChangeHint
&&
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
);
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();
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.
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?");
175 mPendingRestyles
.Get(element
, &data
);
176 NS_ASSERTION(found
, "Where did our entry go?");
178 nsRestyleHint(data
.mRestyleHint
& ~eRestyle_LaterSiblings
);
180 mPendingRestyles
.Put(element
, data
);
183 mHaveLaterSiblingRestyles
= false;
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
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
205 if (!GetRestyleData(element
, &data
)) {
209 ProcessOneRestyle(element
, data
.mRestyleHint
, data
.mChangeHint
);
212 if (mHaveLaterSiblingRestyles
) {
213 // Keep processing restyles for now
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
;
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();
252 mFrameConstructor
->mPresShell
->VerifyStyleTree();
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?");
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.
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
);
286 mPendingRestyles
.Remove(aElement
);
287 aElement
->UnsetFlags(mRestyleBits
);
294 } // namespace mozilla