Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / layout / base / RestyleTracker.cpp
blobd597ebd972c3b3c0e456105ae63a1df247a085db
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
13 * License.
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.
22 * Contributor(s):
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 ***** */
39 /**
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"
48 namespace mozilla {
49 namespace css {
51 inline nsIDocument*
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
75 // document.
76 if (element->GetCurrentDoc() == collector->tracker->Document() &&
77 element->HasFlag(collector->tracker->RestyleBit()) &&
78 (aData.mRestyleHint & eRestyle_LaterSiblings)) {
79 collector->elements->AppendElement(element);
82 return PL_DHASH_NEXT;
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
102 // document.
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 =
130 *restyleArrayPtr;
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;
141 inline void
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,
155 *this,
156 (aRestyleHint & eRestyle_Subtree) != 0);
157 } else if (aChangeHint &&
158 (primaryFrame ||
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);
167 void
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();
186 sibling;
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.
193 break;
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?");
202 RestyleData data;
203 #ifdef DEBUG
204 PRBool found =
205 #endif
206 mPendingRestyles.Get(element, &data);
207 NS_ASSERTION(found, "Where did our entry go?");
208 data.mRestyleHint =
209 nsRestyleHint(data.mRestyleHint & ~eRestyle_LaterSiblings);
211 mPendingRestyles.Put(element, data);
214 mHaveLaterSiblingRestyles = PR_FALSE;
217 PRUint32 rootCount;
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
221 // element.
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
231 // to do here
232 continue;
235 RestyleData data;
236 if (!GetRestyleData(element, &data)) {
237 continue;
240 ProcessOneRestyle(element, data.mRestyleHint, data.mChangeHint);
243 if (mHaveLaterSiblingRestyles) {
244 // Keep processing restyles for now
245 continue;
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;
266 ++currentRestyle) {
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();
280 #ifdef DEBUG
281 mFrameConstructor->mPresShell->VerifyStyleTree();
282 #endif
285 PRBool
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?");
293 return PR_FALSE;
296 #ifdef DEBUG
297 PRBool gotData =
298 #endif
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.
307 RestyleData newData;
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);
314 } else {
315 mPendingRestyles.Remove(aElement);
316 aElement->UnsetFlags(mRestyleBits);
319 return PR_TRUE;
322 } // namespace css
323 } // namespace mozilla