Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 "nsStyleChangeList.h"
13 #include "RestyleManager.h"
14 #include "GeckoProfiler.h"
16 namespace mozilla {
18 inline nsIDocument*
19 RestyleTracker::Document() const {
20 return mRestyleManager->PresContext()->Document();
21 }
23 #define RESTYLE_ARRAY_STACKSIZE 128
25 struct LaterSiblingCollector {
26 RestyleTracker* tracker;
27 nsTArray< nsRefPtr<dom::Element> >* elements;
28 };
30 static PLDHashOperator
31 CollectLaterSiblings(nsISupports* aElement,
32 RestyleTracker::RestyleData& aData,
33 void* aSiblingCollector)
34 {
35 dom::Element* element =
36 static_cast<dom::Element*>(aElement);
37 LaterSiblingCollector* collector =
38 static_cast<LaterSiblingCollector*>(aSiblingCollector);
39 // Only collect the entries that actually need restyling by us (and
40 // haven't, for example, already been restyled).
41 // It's important to not mess with the flags on entries not in our
42 // document.
43 if (element->GetCurrentDoc() == collector->tracker->Document() &&
44 element->HasFlag(collector->tracker->RestyleBit()) &&
45 (aData.mRestyleHint & eRestyle_LaterSiblings)) {
46 collector->elements->AppendElement(element);
47 }
49 return PL_DHASH_NEXT;
50 }
52 struct RestyleCollector {
53 RestyleTracker* tracker;
54 RestyleTracker::RestyleEnumerateData** restyleArrayPtr;
55 };
57 static PLDHashOperator
58 CollectRestyles(nsISupports* aElement,
59 RestyleTracker::RestyleData& aData,
60 void* aRestyleCollector)
61 {
62 dom::Element* element =
63 static_cast<dom::Element*>(aElement);
64 RestyleCollector* collector =
65 static_cast<RestyleCollector*>(aRestyleCollector);
66 // Only collect the entries that actually need restyling by us (and
67 // haven't, for example, already been restyled).
68 // It's important to not mess with the flags on entries not in our
69 // document.
70 if (element->GetCurrentDoc() != collector->tracker->Document() ||
71 !element->HasFlag(collector->tracker->RestyleBit())) {
72 return PL_DHASH_NEXT;
73 }
75 NS_ASSERTION(!element->HasFlag(collector->tracker->RootBit()) ||
76 // Maybe we're just not reachable via the frame tree?
77 (element->GetFlattenedTreeParent() &&
78 (!element->GetFlattenedTreeParent()->GetPrimaryFrame()||
79 element->GetFlattenedTreeParent()->GetPrimaryFrame()->IsLeaf())) ||
80 // Or not reachable due to an async reinsert we have
81 // pending? If so, we'll have a reframe hint around.
82 // That incidentally makes it safe that we still have
83 // the bit, since any descendants that didn't get added
84 // to the roots list because we had the bits will be
85 // completely restyled in a moment.
86 (aData.mChangeHint & nsChangeHint_ReconstructFrame),
87 "Why did this not get handled while processing mRestyleRoots?");
89 // Unset the restyle bits now, so if they get readded later as we
90 // process we won't clobber that adding of the bit.
91 element->UnsetFlags(collector->tracker->RestyleBit() |
92 collector->tracker->RootBit());
94 RestyleTracker::RestyleEnumerateData** restyleArrayPtr =
95 collector->restyleArrayPtr;
96 RestyleTracker::RestyleEnumerateData* currentRestyle =
97 *restyleArrayPtr;
98 currentRestyle->mElement = element;
99 currentRestyle->mRestyleHint = aData.mRestyleHint;
100 currentRestyle->mChangeHint = aData.mChangeHint;
102 // Increment to the next slot in the array
103 *restyleArrayPtr = currentRestyle + 1;
105 return PL_DHASH_NEXT;
106 }
108 inline void
109 RestyleTracker::ProcessOneRestyle(Element* aElement,
110 nsRestyleHint aRestyleHint,
111 nsChangeHint aChangeHint)
112 {
113 NS_PRECONDITION((aRestyleHint & eRestyle_LaterSiblings) == 0,
114 "Someone should have handled this before calling us");
115 NS_PRECONDITION(Document(), "Must have a document");
116 NS_PRECONDITION(aElement->GetCurrentDoc() == Document(),
117 "Element has unexpected document");
119 nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
120 if (aRestyleHint & (eRestyle_Self | eRestyle_Subtree)) {
121 mRestyleManager->RestyleElement(aElement, primaryFrame, aChangeHint,
122 *this,
123 (aRestyleHint & eRestyle_Subtree) != 0);
124 } else if (aChangeHint &&
125 (primaryFrame ||
126 (aChangeHint & nsChangeHint_ReconstructFrame))) {
127 // Don't need to recompute style; just apply the hint
128 nsStyleChangeList changeList;
129 changeList.AppendChange(primaryFrame, aElement, aChangeHint);
130 mRestyleManager->ProcessRestyledFrames(changeList);
131 }
132 }
134 void
135 RestyleTracker::DoProcessRestyles()
136 {
137 PROFILER_LABEL("CSS", "ProcessRestyles");
139 mRestyleManager->BeginProcessingRestyles();
141 // loop so that we process any restyle events generated by processing
142 while (mPendingRestyles.Count()) {
143 if (mHaveLaterSiblingRestyles) {
144 // Convert them to individual restyles on all the later siblings
145 nsAutoTArray<nsRefPtr<Element>, RESTYLE_ARRAY_STACKSIZE> laterSiblingArr;
146 LaterSiblingCollector siblingCollector = { this, &laterSiblingArr };
147 mPendingRestyles.Enumerate(CollectLaterSiblings, &siblingCollector);
148 for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
149 Element* element = laterSiblingArr[i];
150 for (nsIContent* sibling = element->GetNextSibling();
151 sibling;
152 sibling = sibling->GetNextSibling()) {
153 if (sibling->IsElement() &&
154 AddPendingRestyle(sibling->AsElement(), eRestyle_Subtree,
155 NS_STYLE_HINT_NONE)) {
156 // Nothing else to do here; we'll handle the following
157 // siblings when we get to |sibling| in laterSiblingArr.
158 break;
159 }
160 }
161 }
163 // Now remove all those eRestyle_LaterSiblings bits
164 for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
165 Element* element = laterSiblingArr[i];
166 NS_ASSERTION(element->HasFlag(RestyleBit()), "How did that happen?");
167 RestyleData data;
168 #ifdef DEBUG
169 bool found =
170 #endif
171 mPendingRestyles.Get(element, &data);
172 NS_ASSERTION(found, "Where did our entry go?");
173 data.mRestyleHint =
174 nsRestyleHint(data.mRestyleHint & ~eRestyle_LaterSiblings);
176 mPendingRestyles.Put(element, data);
177 }
179 mHaveLaterSiblingRestyles = false;
180 }
182 uint32_t rootCount;
183 while ((rootCount = mRestyleRoots.Length())) {
184 // Make sure to pop the element off our restyle root array, so
185 // that we can freely append to the array as we process this
186 // element.
187 nsRefPtr<Element> element;
188 element.swap(mRestyleRoots[rootCount - 1]);
189 mRestyleRoots.RemoveElementAt(rootCount - 1);
191 // Do the document check before calling GetRestyleData, since we
192 // don't want to do the sibling-processing GetRestyleData does if
193 // the node is no longer relevant.
194 if (element->GetCurrentDoc() != Document()) {
195 // Content node has been removed from our document; nothing else
196 // to do here
197 continue;
198 }
200 RestyleData data;
201 if (!GetRestyleData(element, &data)) {
202 continue;
203 }
205 ProcessOneRestyle(element, data.mRestyleHint, data.mChangeHint);
206 }
208 if (mHaveLaterSiblingRestyles) {
209 // Keep processing restyles for now
210 continue;
211 }
213 // Now we only have entries with change hints left. To be safe in
214 // case of reentry from the handing of the change hint, use a
215 // scratch array instead of calling out to ProcessOneRestyle while
216 // enumerating the hashtable. Use the stack if we can, otherwise
217 // fall back on heap-allocation.
218 nsAutoTArray<RestyleEnumerateData, RESTYLE_ARRAY_STACKSIZE> restyleArr;
219 RestyleEnumerateData* restylesToProcess =
220 restyleArr.AppendElements(mPendingRestyles.Count());
221 if (restylesToProcess) {
222 RestyleEnumerateData* lastRestyle = restylesToProcess;
223 RestyleCollector collector = { this, &lastRestyle };
224 mPendingRestyles.Enumerate(CollectRestyles, &collector);
226 // Clear the hashtable now that we don't need it anymore
227 mPendingRestyles.Clear();
229 for (RestyleEnumerateData* currentRestyle = restylesToProcess;
230 currentRestyle != lastRestyle;
231 ++currentRestyle) {
232 ProcessOneRestyle(currentRestyle->mElement,
233 currentRestyle->mRestyleHint,
234 currentRestyle->mChangeHint);
235 }
236 }
237 }
239 mRestyleManager->EndProcessingRestyles();
240 }
242 bool
243 RestyleTracker::GetRestyleData(Element* aElement, RestyleData* aData)
244 {
245 NS_PRECONDITION(aElement->GetCurrentDoc() == Document(),
246 "Unexpected document; this will lead to incorrect behavior!");
248 if (!aElement->HasFlag(RestyleBit())) {
249 NS_ASSERTION(!aElement->HasFlag(RootBit()), "Bogus root bit?");
250 return false;
251 }
253 #ifdef DEBUG
254 bool gotData =
255 #endif
256 mPendingRestyles.Get(aElement, aData);
257 NS_ASSERTION(gotData, "Must have data if restyle bit is set");
259 if (aData->mRestyleHint & eRestyle_LaterSiblings) {
260 // Someone readded the eRestyle_LaterSiblings hint for this
261 // element. Leave it around for now, but remove the other restyle
262 // hints and the change hint for it. Also unset its root bit,
263 // since it's no longer a root with the new restyle data.
264 RestyleData newData;
265 newData.mChangeHint = nsChangeHint(0);
266 newData.mRestyleHint = eRestyle_LaterSiblings;
267 mPendingRestyles.Put(aElement, newData);
268 aElement->UnsetFlags(RootBit());
269 aData->mRestyleHint =
270 nsRestyleHint(aData->mRestyleHint & ~eRestyle_LaterSiblings);
271 } else {
272 mPendingRestyles.Remove(aElement);
273 aElement->UnsetFlags(mRestyleBits);
274 }
276 return true;
277 }
279 } // namespace mozilla