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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : * Boris Zbarsky <bzbarsky@mit.edu> (original author)
24 : *
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.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : /**
40 : * A class which manages pending restyles. This handles keeping track
41 : * of what nodes restyles need to happen on and so forth.
42 : */
43 :
44 : #include "RestyleTracker.h"
45 : #include "nsCSSFrameConstructor.h"
46 : #include "nsStyleChangeList.h"
47 : #include "sampler.h"
48 :
49 : namespace mozilla {
50 : namespace css {
51 :
52 : inline nsIDocument*
53 0 : RestyleTracker::Document() const {
54 0 : return mFrameConstructor->mDocument;
55 : }
56 :
57 : #define RESTYLE_ARRAY_STACKSIZE 128
58 :
59 : struct LaterSiblingCollector {
60 : RestyleTracker* tracker;
61 : nsTArray< nsRefPtr<dom::Element> >* elements;
62 : };
63 :
64 : static PLDHashOperator
65 0 : CollectLaterSiblings(nsISupports* aElement,
66 : RestyleTracker::RestyleData& aData,
67 : void* aSiblingCollector)
68 : {
69 : dom::Element* element =
70 0 : static_cast<dom::Element*>(aElement);
71 : LaterSiblingCollector* collector =
72 0 : static_cast<LaterSiblingCollector*>(aSiblingCollector);
73 : // Only collect the entries that actually need restyling by us (and
74 : // haven't, for example, already been restyled).
75 : // It's important to not mess with the flags on entries not in our
76 : // document.
77 0 : if (element->GetCurrentDoc() == collector->tracker->Document() &&
78 0 : element->HasFlag(collector->tracker->RestyleBit()) &&
79 : (aData.mRestyleHint & eRestyle_LaterSiblings)) {
80 0 : collector->elements->AppendElement(element);
81 : }
82 :
83 0 : return PL_DHASH_NEXT;
84 : }
85 :
86 : struct RestyleCollector {
87 : RestyleTracker* tracker;
88 : RestyleTracker::RestyleEnumerateData** restyleArrayPtr;
89 : };
90 :
91 : static PLDHashOperator
92 0 : CollectRestyles(nsISupports* aElement,
93 : RestyleTracker::RestyleData& aData,
94 : void* aRestyleCollector)
95 : {
96 : dom::Element* element =
97 0 : static_cast<dom::Element*>(aElement);
98 : RestyleCollector* collector =
99 0 : static_cast<RestyleCollector*>(aRestyleCollector);
100 : // Only collect the entries that actually need restyling by us (and
101 : // haven't, for example, already been restyled).
102 : // It's important to not mess with the flags on entries not in our
103 : // document.
104 0 : if (element->GetCurrentDoc() != collector->tracker->Document() ||
105 0 : !element->HasFlag(collector->tracker->RestyleBit())) {
106 0 : return PL_DHASH_NEXT;
107 : }
108 :
109 0 : NS_ASSERTION(!element->HasFlag(collector->tracker->RootBit()) ||
110 : // Maybe we're just not reachable via the frame tree?
111 : (element->GetFlattenedTreeParent() &&
112 : (!element->GetFlattenedTreeParent()->GetPrimaryFrame()||
113 : element->GetFlattenedTreeParent()->GetPrimaryFrame()->IsLeaf())) ||
114 : // Or not reachable due to an async reinsert we have
115 : // pending? If so, we'll have a reframe hint around.
116 : // That incidentally makes it safe that we still have
117 : // the bit, since any descendants that didn't get added
118 : // to the roots list because we had the bits will be
119 : // completely restyled in a moment.
120 : (aData.mChangeHint & nsChangeHint_ReconstructFrame),
121 : "Why did this not get handled while processing mRestyleRoots?");
122 :
123 : // Unset the restyle bits now, so if they get readded later as we
124 : // process we won't clobber that adding of the bit.
125 0 : element->UnsetFlags(collector->tracker->RestyleBit() |
126 0 : collector->tracker->RootBit());
127 :
128 : RestyleTracker::RestyleEnumerateData** restyleArrayPtr =
129 0 : collector->restyleArrayPtr;
130 : RestyleTracker::RestyleEnumerateData* currentRestyle =
131 0 : *restyleArrayPtr;
132 0 : currentRestyle->mElement = element;
133 0 : currentRestyle->mRestyleHint = aData.mRestyleHint;
134 0 : currentRestyle->mChangeHint = aData.mChangeHint;
135 :
136 : // Increment to the next slot in the array
137 0 : *restyleArrayPtr = currentRestyle + 1;
138 :
139 0 : return PL_DHASH_NEXT;
140 : }
141 :
142 : inline void
143 0 : RestyleTracker::ProcessOneRestyle(Element* aElement,
144 : nsRestyleHint aRestyleHint,
145 : nsChangeHint aChangeHint)
146 : {
147 0 : NS_PRECONDITION((aRestyleHint & eRestyle_LaterSiblings) == 0,
148 : "Someone should have handled this before calling us");
149 0 : NS_PRECONDITION(Document(), "Must have a document");
150 0 : NS_PRECONDITION(aElement->GetCurrentDoc() == Document(),
151 : "Element has unexpected document");
152 :
153 0 : nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
154 0 : if (aRestyleHint & (eRestyle_Self | eRestyle_Subtree)) {
155 : mFrameConstructor->RestyleElement(aElement, primaryFrame, aChangeHint,
156 : *this,
157 0 : (aRestyleHint & eRestyle_Subtree) != 0);
158 0 : } else if (aChangeHint &&
159 : (primaryFrame ||
160 : (aChangeHint & nsChangeHint_ReconstructFrame))) {
161 : // Don't need to recompute style; just apply the hint
162 0 : nsStyleChangeList changeList;
163 0 : changeList.AppendChange(primaryFrame, aElement, aChangeHint);
164 0 : mFrameConstructor->ProcessRestyledFrames(changeList);
165 : }
166 0 : }
167 :
168 : void
169 0 : RestyleTracker::DoProcessRestyles()
170 : {
171 0 : SAMPLE_LABEL("CSS", "ProcessRestyles");
172 : // Make sure to not rebuild quote or counter lists while we're
173 : // processing restyles
174 0 : mFrameConstructor->BeginUpdate();
175 :
176 0 : mFrameConstructor->mInStyleRefresh = true;
177 :
178 : // loop so that we process any restyle events generated by processing
179 0 : while (mPendingRestyles.Count()) {
180 0 : if (mHaveLaterSiblingRestyles) {
181 : // Convert them to individual restyles on all the later siblings
182 0 : nsAutoTArray<nsRefPtr<Element>, RESTYLE_ARRAY_STACKSIZE> laterSiblingArr;
183 0 : LaterSiblingCollector siblingCollector = { this, &laterSiblingArr };
184 0 : mPendingRestyles.Enumerate(CollectLaterSiblings, &siblingCollector);
185 0 : for (PRUint32 i = 0; i < laterSiblingArr.Length(); ++i) {
186 0 : Element* element = laterSiblingArr[i];
187 0 : for (nsIContent* sibling = element->GetNextSibling();
188 : sibling;
189 0 : sibling = sibling->GetNextSibling()) {
190 0 : if (sibling->IsElement() &&
191 : AddPendingRestyle(sibling->AsElement(), eRestyle_Subtree,
192 0 : NS_STYLE_HINT_NONE)) {
193 : // Nothing else to do here; we'll handle the following
194 : // siblings when we get to |sibling| in laterSiblingArr.
195 0 : break;
196 : }
197 : }
198 : }
199 :
200 : // Now remove all those eRestyle_LaterSiblings bits
201 0 : for (PRUint32 i = 0; i < laterSiblingArr.Length(); ++i) {
202 0 : Element* element = laterSiblingArr[i];
203 0 : NS_ASSERTION(element->HasFlag(RestyleBit()), "How did that happen?");
204 : RestyleData data;
205 : #ifdef DEBUG
206 : bool found =
207 : #endif
208 0 : mPendingRestyles.Get(element, &data);
209 0 : NS_ASSERTION(found, "Where did our entry go?");
210 : data.mRestyleHint =
211 0 : nsRestyleHint(data.mRestyleHint & ~eRestyle_LaterSiblings);
212 :
213 0 : mPendingRestyles.Put(element, data);
214 : }
215 :
216 0 : mHaveLaterSiblingRestyles = false;
217 : }
218 :
219 : PRUint32 rootCount;
220 0 : while ((rootCount = mRestyleRoots.Length())) {
221 : // Make sure to pop the element off our restyle root array, so
222 : // that we can freely append to the array as we process this
223 : // element.
224 0 : nsRefPtr<Element> element;
225 0 : element.swap(mRestyleRoots[rootCount - 1]);
226 0 : mRestyleRoots.RemoveElementAt(rootCount - 1);
227 :
228 : // Do the document check before calling GetRestyleData, since we
229 : // don't want to do the sibling-processing GetRestyleData does if
230 : // the node is no longer relevant.
231 0 : if (element->GetCurrentDoc() != Document()) {
232 : // Content node has been removed from our document; nothing else
233 : // to do here
234 0 : continue;
235 : }
236 :
237 : RestyleData data;
238 0 : if (!GetRestyleData(element, &data)) {
239 0 : continue;
240 : }
241 :
242 0 : ProcessOneRestyle(element, data.mRestyleHint, data.mChangeHint);
243 : }
244 :
245 0 : if (mHaveLaterSiblingRestyles) {
246 : // Keep processing restyles for now
247 0 : continue;
248 : }
249 :
250 : // Now we only have entries with change hints left. To be safe in
251 : // case of reentry from the handing of the change hint, use a
252 : // scratch array instead of calling out to ProcessOneRestyle while
253 : // enumerating the hashtable. Use the stack if we can, otherwise
254 : // fall back on heap-allocation.
255 0 : nsAutoTArray<RestyleEnumerateData, RESTYLE_ARRAY_STACKSIZE> restyleArr;
256 : RestyleEnumerateData* restylesToProcess =
257 0 : restyleArr.AppendElements(mPendingRestyles.Count());
258 0 : if (restylesToProcess) {
259 0 : RestyleEnumerateData* lastRestyle = restylesToProcess;
260 0 : RestyleCollector collector = { this, &lastRestyle };
261 0 : mPendingRestyles.Enumerate(CollectRestyles, &collector);
262 :
263 : // Clear the hashtable now that we don't need it anymore
264 0 : mPendingRestyles.Clear();
265 :
266 0 : for (RestyleEnumerateData* currentRestyle = restylesToProcess;
267 : currentRestyle != lastRestyle;
268 : ++currentRestyle) {
269 : ProcessOneRestyle(currentRestyle->mElement,
270 : currentRestyle->mRestyleHint,
271 0 : currentRestyle->mChangeHint);
272 : }
273 : }
274 : }
275 :
276 : // Set mInStyleRefresh to false now, since the EndUpdate call might
277 : // add more restyles.
278 0 : mFrameConstructor->mInStyleRefresh = false;
279 :
280 0 : mFrameConstructor->EndUpdate();
281 :
282 : #ifdef DEBUG
283 0 : mFrameConstructor->mPresShell->VerifyStyleTree();
284 : #endif
285 0 : }
286 :
287 : bool
288 0 : RestyleTracker::GetRestyleData(Element* aElement, RestyleData* aData)
289 : {
290 0 : NS_PRECONDITION(aElement->GetCurrentDoc() == Document(),
291 : "Unexpected document; this will lead to incorrect behavior!");
292 :
293 0 : if (!aElement->HasFlag(RestyleBit())) {
294 0 : NS_ASSERTION(!aElement->HasFlag(RootBit()), "Bogus root bit?");
295 0 : return false;
296 : }
297 :
298 : #ifdef DEBUG
299 : bool gotData =
300 : #endif
301 0 : mPendingRestyles.Get(aElement, aData);
302 0 : NS_ASSERTION(gotData, "Must have data if restyle bit is set");
303 :
304 0 : if (aData->mRestyleHint & eRestyle_LaterSiblings) {
305 : // Someone readded the eRestyle_LaterSiblings hint for this
306 : // element. Leave it around for now, but remove the other restyle
307 : // hints and the change hint for it. Also unset its root bit,
308 : // since it's no longer a root with the new restyle data.
309 : RestyleData newData;
310 0 : newData.mChangeHint = nsChangeHint(0);
311 0 : newData.mRestyleHint = eRestyle_LaterSiblings;
312 0 : mPendingRestyles.Put(aElement, newData);
313 0 : aElement->UnsetFlags(RootBit());
314 : aData->mRestyleHint =
315 0 : nsRestyleHint(aData->mRestyleHint & ~eRestyle_LaterSiblings);
316 : } else {
317 0 : mPendingRestyles.Remove(aElement);
318 0 : aElement->UnsetFlags(mRestyleBits);
319 : }
320 :
321 0 : return true;
322 : }
323 :
324 : } // namespace css
325 : } // namespace mozilla
326 :
|