1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * the Mozilla Foundation.
19 * Portions created by the Initial Developer are Copyright (C) 2010
20 * the Initial Developer. All Rights Reserved.
23 * Boris Zbarsky <bzbarsky@mit.edu> (original author)
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
40 * A class which manages pending restyles. This handles keeping track
41 * of what nodes restyles need to happen on and so forth.
44 #include "RestyleTracker.h"
45 #include "nsCSSFrameConstructor.h"
46 #include "nsStyleChangeList.h"
52 RestyleTracker::Document() const {
53 return mFrameConstructor
->mDocument
;
56 #define RESTYLE_ARRAY_STACKSIZE 128
58 struct LaterSiblingCollector
{
59 RestyleTracker
* tracker
;
60 nsTArray
< nsRefPtr
<dom::Element
> >* elements
;
63 static PLDHashOperator
64 CollectLaterSiblings(nsISupports
* aElement
,
65 RestyleTracker::RestyleData
& aData
,
66 void* aSiblingCollector
)
68 dom::Element
* element
=
69 static_cast<dom::Element
*>(aElement
);
70 LaterSiblingCollector
* collector
=
71 static_cast<LaterSiblingCollector
*>(aSiblingCollector
);
72 // Only collect the entries that actually need restyling by us (and
73 // haven't, for example, already been restyled).
74 // It's important to not mess with the flags on entries not in our
76 if (element
->GetCurrentDoc() == collector
->tracker
->Document() &&
77 element
->HasFlag(collector
->tracker
->RestyleBit()) &&
78 (aData
.mRestyleHint
& eRestyle_LaterSiblings
)) {
79 collector
->elements
->AppendElement(element
);
85 struct RestyleCollector
{
86 RestyleTracker
* tracker
;
87 RestyleTracker::RestyleEnumerateData
** restyleArrayPtr
;
90 static PLDHashOperator
91 CollectRestyles(nsISupports
* aElement
,
92 RestyleTracker::RestyleData
& aData
,
93 void* aRestyleCollector
)
95 dom::Element
* element
=
96 static_cast<dom::Element
*>(aElement
);
97 RestyleCollector
* collector
=
98 static_cast<RestyleCollector
*>(aRestyleCollector
);
99 // Only collect the entries that actually need restyling by us (and
100 // haven't, for example, already been restyled).
101 // It's important to not mess with the flags on entries not in our
103 if (element
->GetCurrentDoc() != collector
->tracker
->Document() ||
104 !element
->HasFlag(collector
->tracker
->RestyleBit())) {
105 return PL_DHASH_NEXT
;
108 NS_ASSERTION(!element
->HasFlag(collector
->tracker
->RootBit()) ||
109 // Maybe we're just not reachable via the frame tree?
110 (element
->GetFlattenedTreeParent() &&
111 (!element
->GetFlattenedTreeParent()->GetPrimaryFrame()||
112 element
->GetFlattenedTreeParent()->GetPrimaryFrame()->IsLeaf())) ||
113 // Or not reachable due to an async reinsert we have
114 // pending? If so, we'll have a reframe hint around.
115 // That incidentally makes it safe that we still have
116 // the bit, since any descendants that didn't get added
117 // to the roots list because we had the bits will be
118 // completely restyled in a moment.
119 (aData
.mChangeHint
& nsChangeHint_ReconstructFrame
),
120 "Why did this not get handled while processing mRestyleRoots?");
122 // Unset the restyle bits now, so if they get readded later as we
123 // process we won't clobber that adding of the bit.
124 element
->UnsetFlags(collector
->tracker
->RestyleBit() |
125 collector
->tracker
->RootBit());
127 RestyleTracker::RestyleEnumerateData
** restyleArrayPtr
=
128 collector
->restyleArrayPtr
;
129 RestyleTracker::RestyleEnumerateData
* currentRestyle
=
131 currentRestyle
->mElement
= element
;
132 currentRestyle
->mRestyleHint
= aData
.mRestyleHint
;
133 currentRestyle
->mChangeHint
= aData
.mChangeHint
;
135 // Increment to the next slot in the array
136 *restyleArrayPtr
= currentRestyle
+ 1;
138 return PL_DHASH_NEXT
;
142 RestyleTracker::ProcessOneRestyle(Element
* aElement
,
143 nsRestyleHint aRestyleHint
,
144 nsChangeHint aChangeHint
)
146 NS_PRECONDITION((aRestyleHint
& eRestyle_LaterSiblings
) == 0,
147 "Someone should have handled this before calling us");
148 NS_PRECONDITION(Document(), "Must have a document");
149 NS_PRECONDITION(aElement
->GetCurrentDoc() == Document(),
150 "Element has unexpected document");
152 nsIFrame
* primaryFrame
= aElement
->GetPrimaryFrame();
153 if (aRestyleHint
& (eRestyle_Self
| eRestyle_Subtree
)) {
154 mFrameConstructor
->RestyleElement(aElement
, primaryFrame
, aChangeHint
,
156 (aRestyleHint
& eRestyle_Subtree
) != 0);
157 } else if (aChangeHint
&&
159 (aChangeHint
& nsChangeHint_ReconstructFrame
))) {
160 // Don't need to recompute style; just apply the hint
161 nsStyleChangeList changeList
;
162 changeList
.AppendChange(primaryFrame
, aElement
, aChangeHint
);
163 mFrameConstructor
->ProcessRestyledFrames(changeList
);
168 RestyleTracker::ProcessRestyles()
170 // Make sure to not rebuild quote or counter lists while we're
171 // processing restyles
172 mFrameConstructor
->BeginUpdate();
174 mFrameConstructor
->mInStyleRefresh
= PR_TRUE
;
176 // loop so that we process any restyle events generated by processing
177 while (mPendingRestyles
.Count()) {
178 if (mHaveLaterSiblingRestyles
) {
179 // Convert them to individual restyles on all the later siblings
180 nsAutoTArray
<nsRefPtr
<Element
>, RESTYLE_ARRAY_STACKSIZE
> laterSiblingArr
;
181 LaterSiblingCollector siblingCollector
= { this, &laterSiblingArr
};
182 mPendingRestyles
.Enumerate(CollectLaterSiblings
, &siblingCollector
);
183 for (PRUint32 i
= 0; i
< laterSiblingArr
.Length(); ++i
) {
184 Element
* element
= laterSiblingArr
[i
];
185 for (nsIContent
* sibling
= element
->GetNextSibling();
187 sibling
= sibling
->GetNextSibling()) {
188 if (sibling
->IsElement() &&
189 AddPendingRestyle(sibling
->AsElement(), eRestyle_Subtree
,
190 NS_STYLE_HINT_NONE
)) {
191 // Nothing else to do here; we'll handle the following
192 // siblings when we get to |sibling| in laterSiblingArr.
198 // Now remove all those eRestyle_LaterSiblings bits
199 for (PRUint32 i
= 0; i
< laterSiblingArr
.Length(); ++i
) {
200 Element
* element
= laterSiblingArr
[i
];
201 NS_ASSERTION(element
->HasFlag(RestyleBit()), "How did that happen?");
206 mPendingRestyles
.Get(element
, &data
);
207 NS_ASSERTION(found
, "Where did our entry go?");
209 nsRestyleHint(data
.mRestyleHint
& ~eRestyle_LaterSiblings
);
211 mPendingRestyles
.Put(element
, data
);
214 mHaveLaterSiblingRestyles
= PR_FALSE
;
218 while ((rootCount
= mRestyleRoots
.Length())) {
219 // Make sure to pop the element off our restyle root array, so
220 // that we can freely append to the array as we process this
222 nsRefPtr
<Element
> element
;
223 element
.swap(mRestyleRoots
[rootCount
- 1]);
224 mRestyleRoots
.RemoveElementAt(rootCount
- 1);
226 // Do the document check before calling GetRestyleData, since we
227 // don't want to do the sibling-processing GetRestyleData does if
228 // the node is no longer relevant.
229 if (element
->GetCurrentDoc() != Document()) {
230 // Content node has been removed from our document; nothing else
236 if (!GetRestyleData(element
, &data
)) {
240 ProcessOneRestyle(element
, data
.mRestyleHint
, data
.mChangeHint
);
243 if (mHaveLaterSiblingRestyles
) {
244 // Keep processing restyles for now
248 // Now we only have entries with change hints left. To be safe in
249 // case of reentry from the handing of the change hint, use a
250 // scratch array instead of calling out to ProcessOneRestyle while
251 // enumerating the hashtable. Use the stack if we can, otherwise
252 // fall back on heap-allocation.
253 nsAutoTArray
<RestyleEnumerateData
, RESTYLE_ARRAY_STACKSIZE
> restyleArr
;
254 RestyleEnumerateData
* restylesToProcess
=
255 restyleArr
.AppendElements(mPendingRestyles
.Count());
256 if (restylesToProcess
) {
257 RestyleEnumerateData
* lastRestyle
= restylesToProcess
;
258 RestyleCollector collector
= { this, &lastRestyle
};
259 mPendingRestyles
.Enumerate(CollectRestyles
, &collector
);
261 // Clear the hashtable now that we don't need it anymore
262 mPendingRestyles
.Clear();
264 for (RestyleEnumerateData
* currentRestyle
= restylesToProcess
;
265 currentRestyle
!= lastRestyle
;
267 ProcessOneRestyle(currentRestyle
->mElement
,
268 currentRestyle
->mRestyleHint
,
269 currentRestyle
->mChangeHint
);
274 // Set mInStyleRefresh to false now, since the EndUpdate call might
275 // add more restyles.
276 mFrameConstructor
->mInStyleRefresh
= PR_FALSE
;
278 mFrameConstructor
->EndUpdate();
281 mFrameConstructor
->mPresShell
->VerifyStyleTree();
286 RestyleTracker::GetRestyleData(Element
* aElement
, RestyleData
* aData
)
288 NS_PRECONDITION(aElement
->GetCurrentDoc() == Document(),
289 "Unexpected document; this will lead to incorrect behavior!");
291 if (!aElement
->HasFlag(RestyleBit())) {
292 NS_ASSERTION(!aElement
->HasFlag(RootBit()), "Bogus root bit?");
299 mPendingRestyles
.Get(aElement
, aData
);
300 NS_ASSERTION(gotData
, "Must have data if restyle bit is set");
302 if (aData
->mRestyleHint
& eRestyle_LaterSiblings
) {
303 // Someone readded the eRestyle_LaterSiblings hint for this
304 // element. Leave it around for now, but remove the other restyle
305 // hints and the change hint for it. Also unset its root bit,
306 // since it's no longer a root with the new restyle data.
308 newData
.mChangeHint
= nsChangeHint(0);
309 newData
.mRestyleHint
= eRestyle_LaterSiblings
;
310 mPendingRestyles
.Put(aElement
, newData
);
311 aElement
->UnsetFlags(RootBit());
312 aData
->mRestyleHint
=
313 nsRestyleHint(aData
->mRestyleHint
& ~eRestyle_LaterSiblings
);
315 mPendingRestyles
.Remove(aElement
);
316 aElement
->UnsetFlags(mRestyleBits
);
323 } // namespace mozilla