Line data Source code
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/. */
5 :
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 : */
10 :
11 : #ifndef mozilla_RestyleTracker_h
12 : #define mozilla_RestyleTracker_h
13 :
14 : #include "mozilla/dom/Element.h"
15 : #include "mozilla/OverflowChangedTracker.h"
16 : #include "nsAutoPtr.h"
17 : #include "nsClassHashtable.h"
18 : #include "nsContainerFrame.h"
19 : #include "nsIContentInlines.h"
20 : #include "mozilla/SplayTree.h"
21 : #include "mozilla/RestyleLogging.h"
22 : #include "GeckoProfiler.h"
23 : #include "mozilla/Maybe.h"
24 :
25 : namespace mozilla {
26 :
27 : class ElementRestyler;
28 : class GeckoRestyleManager;
29 :
30 5 : class RestyleTracker {
31 : public:
32 : typedef mozilla::dom::Element Element;
33 :
34 : friend class ElementRestyler; // for AddPendingRestyleToTable
35 :
36 29 : explicit RestyleTracker(Element::FlagsType aRestyleBits)
37 29 : : mRestyleBits(aRestyleBits)
38 : , mHaveLaterSiblingRestyles(false)
39 29 : , mHaveSelectors(false)
40 : {
41 29 : NS_PRECONDITION((mRestyleBits & ~ELEMENT_ALL_RESTYLE_FLAGS) == 0,
42 : "Why do we have these bits set?");
43 29 : NS_PRECONDITION((mRestyleBits & ELEMENT_PENDING_RESTYLE_FLAGS) != 0,
44 : "Must have a restyle flag");
45 29 : NS_PRECONDITION((mRestyleBits & ELEMENT_PENDING_RESTYLE_FLAGS) !=
46 : ELEMENT_PENDING_RESTYLE_FLAGS,
47 : "Shouldn't have both restyle flags set");
48 29 : NS_PRECONDITION((mRestyleBits & ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS) != 0,
49 : "Must have root flag");
50 29 : NS_PRECONDITION((mRestyleBits & ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS) !=
51 : ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS,
52 : "Shouldn't have both root flags");
53 29 : }
54 :
55 29 : void Init(GeckoRestyleManager* aRestyleManager) {
56 29 : mRestyleManager = aRestyleManager;
57 29 : }
58 :
59 267 : uint32_t Count() const {
60 267 : return mPendingRestyles.Count();
61 : }
62 :
63 : /**
64 : * Add a restyle for the given element to the tracker. Returns true
65 : * if the element already had eRestyle_LaterSiblings set on it.
66 : *
67 : * aRestyleRoot is the closest restyle root for aElement. If the caller
68 : * does not know what the closest restyle root is, Nothing should be
69 : * passed. A Some(nullptr) restyle root can be passed if there is no
70 : * ancestor element that is a restyle root.
71 : */
72 : bool AddPendingRestyle(Element* aElement, nsRestyleHint aRestyleHint,
73 : nsChangeHint aMinChangeHint,
74 : const RestyleHintData* aRestyleHintData = nullptr,
75 : const mozilla::Maybe<Element*>& aRestyleRoot =
76 : mozilla::Nothing());
77 :
78 : Element* FindClosestRestyleRoot(Element* aElement);
79 :
80 : /**
81 : * Process the restyles we've been tracking.
82 : */
83 : void DoProcessRestyles();
84 :
85 : // Return our ELEMENT_HAS_PENDING_(ANIMATION_)RESTYLE bit
86 2226 : uint32_t RestyleBit() const {
87 2226 : return mRestyleBits & ELEMENT_PENDING_RESTYLE_FLAGS;
88 : }
89 :
90 : // Return our ELEMENT_IS_POTENTIAL_(ANIMATION_)RESTYLE_ROOT bit
91 3665 : Element::FlagsType RootBit() const {
92 3665 : return mRestyleBits & ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS;
93 : }
94 :
95 : // Return our ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR bit if present,
96 : // or 0 if it is not.
97 100 : Element::FlagsType ConditionalDescendantsBit() const {
98 100 : return mRestyleBits & ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR;
99 : }
100 :
101 360 : struct Hints {
102 : nsRestyleHint mRestyleHint; // What we want to restyle
103 : nsChangeHint mChangeHint; // The minimal change hint for "self"
104 : RestyleHintData mRestyleHintData; // Data associated with mRestyleHint
105 : };
106 :
107 161 : struct RestyleData : Hints {
108 0 : RestyleData() {
109 0 : mRestyleHint = nsRestyleHint(0);
110 0 : mChangeHint = nsChangeHint(0);
111 0 : }
112 :
113 161 : RestyleData(nsRestyleHint aRestyleHint, nsChangeHint aChangeHint,
114 161 : const RestyleHintData* aRestyleHintData) {
115 161 : mRestyleHint = aRestyleHint;
116 161 : mChangeHint = aChangeHint;
117 161 : if (aRestyleHintData) {
118 95 : mRestyleHintData = *aRestyleHintData;
119 : }
120 161 : }
121 :
122 : // Descendant elements we must check that we ended up restyling, ordered
123 : // with the same invariant as mRestyleRoots. The elements here are those
124 : // that we called AddPendingRestyle for and found the element this is
125 : // the RestyleData for as its nearest restyle root.
126 : nsTArray<RefPtr<Element>> mDescendants;
127 : UniqueProfilerBacktrace mBacktrace;
128 : };
129 :
130 : /**
131 : * If the given Element has a restyle pending for it, return the
132 : * relevant restyle data. This function will clear everything other
133 : * than a possible eRestyle_LaterSiblings hint for aElement out of
134 : * our hashtable. The returned aData will never have an
135 : * eRestyle_LaterSiblings hint in it.
136 : *
137 : * The return value indicates whether any restyle data was found for
138 : * the element. aData is set to nullptr iff false is returned.
139 : */
140 : bool GetRestyleData(Element* aElement, nsAutoPtr<RestyleData>& aData);
141 :
142 : /**
143 : * Returns whether there is a RestyleData entry in mPendingRestyles
144 : * for the given element.
145 : */
146 276 : bool HasRestyleData(Element* aElement) {
147 276 : return mPendingRestyles.Contains(aElement);
148 : }
149 :
150 : /**
151 : * For each element in aElements, appends it to mRestyleRoots if it
152 : * has its restyle bit set. This is used to ensure we restyle elements
153 : * that we did not add as restyle roots initially (due to there being
154 : * an ancestor with the restyle root bit set), but which we might
155 : * not have got around to restyling due to the restyle process
156 : * terminating early with RestyleResul::eStop (see ElementRestyler::Restyle).
157 : *
158 : * This function must be called with elements in order such that
159 : * appending them to mRestyleRoots maintains its ordering invariant that
160 : * ancestors appear after descendants.
161 : */
162 : void AddRestyleRootsIfAwaitingRestyle(
163 : const nsTArray<RefPtr<Element>>& aElements);
164 :
165 : /**
166 : * Converts any eRestyle_SomeDescendants restyle hints in the pending restyle
167 : * table into eRestyle_Subtree hints and clears out the associated arrays of
168 : * nsCSSSelector pointers. This is called in response to a style sheet change
169 : * that might have cause an nsCSSSelector to be destroyed.
170 : */
171 : void ClearSelectors();
172 :
173 : /**
174 : * The document we're associated with.
175 : */
176 : inline nsIDocument* Document() const;
177 :
178 : #ifdef RESTYLE_LOGGING
179 : // Defined in RestyleTrackerInlines.h.
180 : inline bool ShouldLogRestyle();
181 : inline int32_t& LoggingDepth();
182 : #endif
183 :
184 : private:
185 : bool AddPendingRestyleToTable(Element* aElement, nsRestyleHint aRestyleHint,
186 : nsChangeHint aMinChangeHint,
187 : const RestyleHintData* aRestyleHintData = nullptr);
188 :
189 : /**
190 : * Handle a single mPendingRestyles entry. aRestyleHint must not
191 : * include eRestyle_LaterSiblings; that needs to be dealt with
192 : * before calling this function.
193 : */
194 : inline void ProcessOneRestyle(Element* aElement,
195 : nsRestyleHint aRestyleHint,
196 : nsChangeHint aChangeHint,
197 : const RestyleHintData& aRestyleHintData);
198 :
199 : typedef nsClassHashtable<nsISupportsHashKey, RestyleData> PendingRestyleTable;
200 : typedef AutoTArray< RefPtr<Element>, 32> RestyleRootArray;
201 : // Our restyle bits. These will be a subset of ELEMENT_ALL_RESTYLE_FLAGS, and
202 : // will include one flag from ELEMENT_PENDING_RESTYLE_FLAGS, one flag
203 : // from ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS, and might also include
204 : // ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR.
205 : Element::FlagsType mRestyleBits;
206 : GeckoRestyleManager* mRestyleManager; // Owns us
207 : // A hashtable that maps elements to pointers to RestyleData structs. The
208 : // values only make sense if the element's current document is our
209 : // document and it has our RestyleBit() flag set. In particular,
210 : // said bit might not be set if the element had a restyle posted and
211 : // then was moved around in the DOM.
212 : PendingRestyleTable mPendingRestyles;
213 : // An array that keeps track of our possible restyle roots. This
214 : // maintains the invariant that if A and B are both restyle roots
215 : // and A is an ancestor of B then A will come after B in the array.
216 : // We maintain this invariant by checking whether an element has an
217 : // ancestor with the restyle root bit set before appending it to the
218 : // array.
219 : RestyleRootArray mRestyleRoots;
220 : // True if we have some entries with the eRestyle_LaterSiblings
221 : // flag. We need this to avoid enumerating the hashtable looking
222 : // for such entries when we can't possibly have any.
223 : bool mHaveLaterSiblingRestyles;
224 : // True if we have some entries with selectors in the restyle hint data.
225 : // We use this to skip iterating over mPendingRestyles in ClearSelectors.
226 : bool mHaveSelectors;
227 : };
228 :
229 : inline bool
230 228 : RestyleTracker::AddPendingRestyleToTable(Element* aElement,
231 : nsRestyleHint aRestyleHint,
232 : nsChangeHint aMinChangeHint,
233 : const RestyleHintData* aRestyleHintData)
234 : {
235 : RestyleData* existingData;
236 :
237 365 : if (aRestyleHintData &&
238 137 : !aRestyleHintData->mSelectorsForDescendants.IsEmpty()) {
239 41 : mHaveSelectors = true;
240 : }
241 :
242 : // Check the RestyleBit() flag before doing the hashtable Get, since
243 : // it's possible that the data in the hashtable isn't actually
244 : // relevant anymore (if the flag is not set).
245 228 : if (aElement->HasFlag(RestyleBit())) {
246 67 : mPendingRestyles.Get(aElement, &existingData);
247 : } else {
248 161 : aElement->SetFlags(RestyleBit());
249 161 : existingData = nullptr;
250 : }
251 :
252 228 : if (aRestyleHint & eRestyle_SomeDescendants) {
253 43 : NS_ASSERTION(ConditionalDescendantsBit(),
254 : "why are we getting eRestyle_SomeDescendants in an "
255 : "animation-only restyle?");
256 43 : aElement->SetFlags(ConditionalDescendantsBit());
257 : }
258 :
259 228 : if (!existingData) {
260 : RestyleData* rd =
261 161 : new RestyleData(aRestyleHint, aMinChangeHint, aRestyleHintData);
262 161 : if (profiler_feature_active(ProfilerFeature::Restyle)) {
263 0 : rd->mBacktrace = profiler_get_backtrace();
264 : }
265 161 : mPendingRestyles.Put(aElement, rd);
266 161 : return false;
267 : }
268 :
269 : bool hadRestyleLaterSiblings =
270 67 : (existingData->mRestyleHint & eRestyle_LaterSiblings) != 0;
271 134 : existingData->mRestyleHint =
272 67 : nsRestyleHint(existingData->mRestyleHint | aRestyleHint);
273 67 : existingData->mChangeHint |= aMinChangeHint;
274 67 : if (aRestyleHintData) {
275 : existingData->mRestyleHintData.mSelectorsForDescendants
276 42 : .AppendElements(aRestyleHintData->mSelectorsForDescendants);
277 : }
278 :
279 67 : return hadRestyleLaterSiblings;
280 : }
281 :
282 : inline mozilla::dom::Element*
283 216 : RestyleTracker::FindClosestRestyleRoot(Element* aElement)
284 : {
285 216 : Element* cur = aElement;
286 2318 : while (!cur->HasFlag(RootBit())) {
287 1159 : nsIContent* parent = cur->GetFlattenedTreeParent();
288 : // Stop if we have no parent or the parent is not an element or
289 : // we're part of the viewport scrollbars (because those are not
290 : // frametree descendants of the primary frame of the root
291 : // element).
292 : // XXXbz maybe the primary frame of the root should be the root scrollframe?
293 2322 : if (!parent || !parent->IsElement() ||
294 : // If we've hit the root via a native anonymous kid and that
295 : // this native anonymous kid is not obviously a descendant
296 : // of the root's primary frame, assume we're under the root
297 : // scrollbars. Since those don't get reresolved when
298 : // reresolving the root, we need to make sure to add the
299 : // element to mRestyleRoots.
300 1071 : (cur->IsInNativeAnonymousSubtree() && !parent->GetParent() &&
301 8 : cur->GetPrimaryFrame() &&
302 4 : cur->GetPrimaryFrame()->GetParent() != parent->GetPrimaryFrame())) {
303 108 : return nullptr;
304 : }
305 1051 : cur = parent->AsElement();
306 : }
307 108 : return cur;
308 : }
309 :
310 : inline bool
311 228 : RestyleTracker::AddPendingRestyle(Element* aElement,
312 : nsRestyleHint aRestyleHint,
313 : nsChangeHint aMinChangeHint,
314 : const RestyleHintData* aRestyleHintData,
315 : const mozilla::Maybe<Element*>& aRestyleRoot)
316 : {
317 : bool hadRestyleLaterSiblings =
318 228 : AddPendingRestyleToTable(aElement, aRestyleHint, aMinChangeHint,
319 228 : aRestyleHintData);
320 :
321 : // We can only treat this element as a restyle root if we would
322 : // actually restyle its descendants (so either call
323 : // ElementRestyler::Restyle on it or just reframe it).
324 265 : if ((aRestyleHint & ~eRestyle_LaterSiblings) ||
325 37 : (aMinChangeHint & nsChangeHint_ReconstructFrame)) {
326 : Element* cur =
327 196 : aRestyleRoot ? *aRestyleRoot : FindClosestRestyleRoot(aElement);
328 196 : if (!cur) {
329 84 : mRestyleRoots.AppendElement(aElement);
330 84 : cur = aElement;
331 : }
332 : // At this point some ancestor of aElement (possibly aElement
333 : // itself) is in mRestyleRoots. Set the root bit on aElement, to
334 : // speed up searching for an existing root on its descendants.
335 196 : aElement->SetFlags(RootBit());
336 196 : if (cur != aElement) {
337 : // We are already going to restyle cur, one of aElement's ancestors,
338 : // but we might not end up restyling all the way down to aElement.
339 : // Record it in the RestyleData so we can ensure it does get restyled
340 : // after we deal with cur.
341 : //
342 : // As with the mRestyleRoots array, mDescendants maintains the
343 : // invariant that if two elements appear in the array and one
344 : // is an ancestor of the other, that the ancestor appears after
345 : // the descendant.
346 : RestyleData* curData;
347 46 : mPendingRestyles.Get(cur, &curData);
348 :
349 : // Even if cur has a ForceDescendants restyle hint, we're not guaranteed
350 : // to reach aElement in the case the PresShell posts a restyle event from
351 : // PostRecreateFramesFor, so we need to track it here.
352 46 : MOZ_ASSERT(curData, "expected to find a RestyleData for cur");
353 46 : if (curData) {
354 46 : curData->mDescendants.AppendElement(aElement);
355 : }
356 : }
357 : }
358 :
359 228 : mHaveLaterSiblingRestyles =
360 228 : mHaveLaterSiblingRestyles || (aRestyleHint & eRestyle_LaterSiblings) != 0;
361 228 : return hadRestyleLaterSiblings;
362 : }
363 :
364 : } // namespace mozilla
365 :
366 : #endif /* mozilla_RestyleTracker_h */
|