|
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 * Code responsible for managing style changes: tracking what style |
|
8 * changes need to happen, scheduling them, and doing them. |
|
9 */ |
|
10 |
|
11 #ifndef mozilla_RestyleManager_h |
|
12 #define mozilla_RestyleManager_h |
|
13 |
|
14 #include "nsISupportsImpl.h" |
|
15 #include "nsChangeHint.h" |
|
16 #include "RestyleTracker.h" |
|
17 #include "nsPresContext.h" |
|
18 |
|
19 class nsRefreshDriver; |
|
20 class nsIFrame; |
|
21 struct TreeMatchContext; |
|
22 |
|
23 namespace mozilla { |
|
24 class EventStates; |
|
25 |
|
26 namespace dom { |
|
27 class Element; |
|
28 } // namespace dom |
|
29 |
|
30 class RestyleManager MOZ_FINAL { |
|
31 public: |
|
32 friend class ::nsRefreshDriver; |
|
33 friend class RestyleTracker; |
|
34 |
|
35 typedef mozilla::dom::Element Element; |
|
36 |
|
37 RestyleManager(nsPresContext* aPresContext); |
|
38 |
|
39 private: |
|
40 // Private destructor, to discourage deletion outside of Release(): |
|
41 ~RestyleManager() |
|
42 { |
|
43 } |
|
44 |
|
45 public: |
|
46 NS_INLINE_DECL_REFCOUNTING(mozilla::RestyleManager) |
|
47 |
|
48 void Disconnect() { |
|
49 mPresContext = nullptr; |
|
50 } |
|
51 |
|
52 nsPresContext* PresContext() const { |
|
53 MOZ_ASSERT(mPresContext); |
|
54 return mPresContext; |
|
55 } |
|
56 |
|
57 nsCSSFrameConstructor* FrameConstructor() const |
|
58 { return PresContext()->FrameConstructor(); } |
|
59 |
|
60 // Should be called when a frame is going to be destroyed and |
|
61 // WillDestroyFrameTree hasn't been called yet. |
|
62 void NotifyDestroyingFrame(nsIFrame* aFrame); |
|
63 |
|
64 // Forwarded nsIDocumentObserver method, to handle restyling (and |
|
65 // passing the notification to the frame). |
|
66 nsresult ContentStateChanged(nsIContent* aContent, |
|
67 EventStates aStateMask); |
|
68 |
|
69 // Forwarded nsIMutationObserver method, to handle restyling. |
|
70 void AttributeWillChange(Element* aElement, |
|
71 int32_t aNameSpaceID, |
|
72 nsIAtom* aAttribute, |
|
73 int32_t aModType); |
|
74 // Forwarded nsIMutationObserver method, to handle restyling (and |
|
75 // passing the notification to the frame). |
|
76 void AttributeChanged(Element* aElement, |
|
77 int32_t aNameSpaceID, |
|
78 nsIAtom* aAttribute, |
|
79 int32_t aModType); |
|
80 |
|
81 // Get an integer that increments every time there is a style change |
|
82 // as a result of a change to the :hover content state. |
|
83 uint32_t GetHoverGeneration() const { return mHoverGeneration; } |
|
84 |
|
85 // Get a counter that increments on every style change, that we use to |
|
86 // track whether off-main-thread animations are up-to-date. |
|
87 uint64_t GetAnimationGeneration() const { return mAnimationGeneration; } |
|
88 |
|
89 /** |
|
90 * Reparent the style contexts of this frame subtree. The parent frame of |
|
91 * aFrame must be changed to the new parent before this function is called; |
|
92 * the new parent style context will be automatically computed based on the |
|
93 * new position in the frame tree. |
|
94 * |
|
95 * @param aFrame the root of the subtree to reparent. Must not be null. |
|
96 */ |
|
97 NS_HIDDEN_(nsresult) ReparentStyleContext(nsIFrame* aFrame); |
|
98 |
|
99 /** |
|
100 * Re-resolve the style contexts for a frame tree, building |
|
101 * aChangeList based on the resulting style changes, plus aMinChange |
|
102 * applied to aFrame. |
|
103 */ |
|
104 NS_HIDDEN_(void) |
|
105 ComputeStyleChangeFor(nsIFrame* aFrame, |
|
106 nsStyleChangeList* aChangeList, |
|
107 nsChangeHint aMinChange, |
|
108 RestyleTracker& aRestyleTracker, |
|
109 bool aRestyleDescendants); |
|
110 |
|
111 #ifdef DEBUG |
|
112 /** |
|
113 * DEBUG ONLY method to verify integrity of style tree versus frame tree |
|
114 */ |
|
115 NS_HIDDEN_(void) DebugVerifyStyleTree(nsIFrame* aFrame); |
|
116 #endif |
|
117 |
|
118 // Note: It's the caller's responsibility to make sure to wrap a |
|
119 // ProcessRestyledFrames call in a view update batch and a script blocker. |
|
120 // This function does not call ProcessAttachedQueue() on the binding manager. |
|
121 // If the caller wants that to happen synchronously, it needs to handle that |
|
122 // itself. |
|
123 nsresult ProcessRestyledFrames(nsStyleChangeList& aRestyleArray); |
|
124 |
|
125 private: |
|
126 void RestyleForEmptyChange(Element* aContainer); |
|
127 |
|
128 public: |
|
129 // Restyling for a ContentInserted (notification after insertion) or |
|
130 // for a CharacterDataChanged. |aContainer| must be non-null; when |
|
131 // the container is null, no work is needed. |
|
132 void RestyleForInsertOrChange(Element* aContainer, nsIContent* aChild); |
|
133 |
|
134 // This would be the same as RestyleForInsertOrChange if we got the |
|
135 // notification before the removal. However, we get it after, so we need the |
|
136 // following sibling in addition to the old child. |aContainer| must be |
|
137 // non-null; when the container is null, no work is needed. aFollowingSibling |
|
138 // is the sibling that used to come after aOldChild before the removal. |
|
139 void RestyleForRemove(Element* aContainer, |
|
140 nsIContent* aOldChild, |
|
141 nsIContent* aFollowingSibling); |
|
142 |
|
143 // Same for a ContentAppended. |aContainer| must be non-null; when |
|
144 // the container is null, no work is needed. |
|
145 void RestyleForAppend(Element* aContainer, nsIContent* aFirstNewContent); |
|
146 |
|
147 // Process any pending restyles. This should be called after |
|
148 // CreateNeededFrames. |
|
149 // Note: It's the caller's responsibility to make sure to wrap a |
|
150 // ProcessPendingRestyles call in a view update batch and a script blocker. |
|
151 // This function does not call ProcessAttachedQueue() on the binding manager. |
|
152 // If the caller wants that to happen synchronously, it needs to handle that |
|
153 // itself. |
|
154 void ProcessPendingRestyles(); |
|
155 |
|
156 // ProcessPendingRestyles calls into one of our RestyleTracker |
|
157 // objects. It then calls back to these functions at the beginning |
|
158 // and end of its work. |
|
159 void BeginProcessingRestyles(); |
|
160 void EndProcessingRestyles(); |
|
161 |
|
162 // Rebuilds all style data by throwing out the old rule tree and |
|
163 // building a new one, and additionally applying aExtraHint (which |
|
164 // must not contain nsChangeHint_ReconstructFrame) to the root frame. |
|
165 void RebuildAllStyleData(nsChangeHint aExtraHint); |
|
166 |
|
167 // Helper that does part of the work of RebuildAllStyleData, shared by |
|
168 // RestyleElement for 'rem' handling. |
|
169 void DoRebuildAllStyleData(RestyleTracker& aRestyleTracker, |
|
170 nsChangeHint aExtraHint); |
|
171 |
|
172 // See PostRestyleEventCommon below. |
|
173 void PostRestyleEvent(Element* aElement, |
|
174 nsRestyleHint aRestyleHint, |
|
175 nsChangeHint aMinChangeHint) |
|
176 { |
|
177 if (mPresContext) { |
|
178 PostRestyleEventCommon(aElement, aRestyleHint, aMinChangeHint, |
|
179 mPresContext->IsProcessingAnimationStyleChange()); |
|
180 } |
|
181 } |
|
182 |
|
183 // See PostRestyleEventCommon below. |
|
184 void PostAnimationRestyleEvent(Element* aElement, |
|
185 nsRestyleHint aRestyleHint, |
|
186 nsChangeHint aMinChangeHint) |
|
187 { |
|
188 PostRestyleEventCommon(aElement, aRestyleHint, aMinChangeHint, true); |
|
189 } |
|
190 |
|
191 void PostRestyleEventForLazyConstruction() |
|
192 { |
|
193 PostRestyleEventInternal(true); |
|
194 } |
|
195 |
|
196 void FlushOverflowChangedTracker() |
|
197 { |
|
198 mOverflowChangedTracker.Flush(); |
|
199 } |
|
200 |
|
201 private: |
|
202 /** |
|
203 * Notify the frame constructor that an element needs to have its |
|
204 * style recomputed. |
|
205 * @param aElement: The element to be restyled. |
|
206 * @param aRestyleHint: Which nodes need to have selector matching run |
|
207 * on them. |
|
208 * @param aMinChangeHint: A minimum change hint for aContent and its |
|
209 * descendants. |
|
210 * @param aForAnimation: Whether the style should be computed with or |
|
211 * without animation data. Animation code |
|
212 * sometimes needs to pass true; other code |
|
213 * should generally pass the the pres context's |
|
214 * IsProcessingAnimationStyleChange() value |
|
215 * (which is the default value). |
|
216 */ |
|
217 void PostRestyleEventCommon(Element* aElement, |
|
218 nsRestyleHint aRestyleHint, |
|
219 nsChangeHint aMinChangeHint, |
|
220 bool aForAnimation); |
|
221 void PostRestyleEventInternal(bool aForLazyConstruction); |
|
222 |
|
223 public: |
|
224 /** |
|
225 * Asynchronously clear style data from the root frame downwards and ensure |
|
226 * it will all be rebuilt. This is safe to call anytime; it will schedule |
|
227 * a restyle and take effect next time style changes are flushed. |
|
228 * This method is used to recompute the style data when some change happens |
|
229 * outside of any style rules, like a color preference change or a change |
|
230 * in a system font size, or to fix things up when an optimization in the |
|
231 * style data has become invalid. We assume that the root frame will not |
|
232 * need to be reframed. |
|
233 */ |
|
234 void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint); |
|
235 |
|
236 private: |
|
237 /* aMinHint is the minimal change that should be made to the element */ |
|
238 // XXXbz do we really need the aPrimaryFrame argument here? |
|
239 void RestyleElement(Element* aElement, |
|
240 nsIFrame* aPrimaryFrame, |
|
241 nsChangeHint aMinHint, |
|
242 RestyleTracker& aRestyleTracker, |
|
243 bool aRestyleDescendants); |
|
244 |
|
245 nsresult StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint); |
|
246 |
|
247 // Returns true if this function managed to successfully move a frame, and |
|
248 // false if it could not process the position change, and a reflow should |
|
249 // be performed instead. |
|
250 bool RecomputePosition(nsIFrame* aFrame); |
|
251 |
|
252 private: |
|
253 nsPresContext* mPresContext; // weak, disconnected in Disconnect |
|
254 |
|
255 bool mRebuildAllStyleData : 1; |
|
256 // True if we're already waiting for a refresh notification |
|
257 bool mObservingRefreshDriver : 1; |
|
258 // True if we're in the middle of a nsRefreshDriver refresh |
|
259 bool mInStyleRefresh : 1; |
|
260 uint32_t mHoverGeneration; |
|
261 nsChangeHint mRebuildAllExtraHint; |
|
262 |
|
263 OverflowChangedTracker mOverflowChangedTracker; |
|
264 |
|
265 // The total number of animation flushes by this frame constructor. |
|
266 // Used to keep the layer and animation manager in sync. |
|
267 uint64_t mAnimationGeneration; |
|
268 |
|
269 RestyleTracker mPendingRestyles; |
|
270 RestyleTracker mPendingAnimationRestyles; |
|
271 }; |
|
272 |
|
273 /** |
|
274 * An ElementRestyler is created for *each* element in a subtree that we |
|
275 * recompute styles for. |
|
276 */ |
|
277 class ElementRestyler MOZ_FINAL { |
|
278 public: |
|
279 typedef mozilla::dom::Element Element; |
|
280 |
|
281 // Construct for the root of the subtree that we're restyling. |
|
282 ElementRestyler(nsPresContext* aPresContext, |
|
283 nsIFrame* aFrame, |
|
284 nsStyleChangeList* aChangeList, |
|
285 nsChangeHint aHintsHandledByAncestors, |
|
286 RestyleTracker& aRestyleTracker, |
|
287 TreeMatchContext& aTreeMatchContext, |
|
288 nsTArray<nsIContent*>& aVisibleKidsOfHiddenElement); |
|
289 |
|
290 // Construct for an element whose parent is being restyled. |
|
291 enum ConstructorFlags { |
|
292 FOR_OUT_OF_FLOW_CHILD = 1<<0 |
|
293 }; |
|
294 ElementRestyler(const ElementRestyler& aParentRestyler, |
|
295 nsIFrame* aFrame, |
|
296 uint32_t aConstructorFlags); |
|
297 |
|
298 // Construct for a frame whose parent is being restyled, but whose |
|
299 // style context is the parent style context for its parent frame. |
|
300 // (This is only used for table frames, whose style contexts are used |
|
301 // as the parent style context for their outer table frame (table |
|
302 // wrapper frame). We should probably try to get rid of this |
|
303 // exception and have the inheritance go the other way.) |
|
304 enum ParentContextFromChildFrame { PARENT_CONTEXT_FROM_CHILD_FRAME }; |
|
305 ElementRestyler(ParentContextFromChildFrame, |
|
306 const ElementRestyler& aParentFrameRestyler, |
|
307 nsIFrame* aFrame); |
|
308 |
|
309 /** |
|
310 * Restyle our frame's element and its subtree. |
|
311 * |
|
312 * Use eRestyle_Self for the aRestyleHint argument to mean |
|
313 * "reresolve our style context but not kids", use eRestyle_Subtree |
|
314 * to mean "reresolve our style context and kids", and use |
|
315 * nsRestyleHint(0) to mean recompute a new style context for our |
|
316 * current parent and existing rulenode, and the same for kids. |
|
317 */ |
|
318 void Restyle(nsRestyleHint aRestyleHint); |
|
319 |
|
320 /** |
|
321 * mHintsHandled changes over time; it starts off as the hints that |
|
322 * have been handled by ancestors, and by the end of Restyle it |
|
323 * represents the hints that have been handled for this frame. This |
|
324 * method is intended to be called after Restyle, to find out what |
|
325 * hints have been handled for this frame. |
|
326 */ |
|
327 nsChangeHint HintsHandledForFrame() { return mHintsHandled; } |
|
328 |
|
329 private: |
|
330 /** |
|
331 * First half of Restyle(). |
|
332 */ |
|
333 void RestyleSelf(nsIFrame* aSelf, nsRestyleHint aRestyleHint); |
|
334 |
|
335 /** |
|
336 * Restyle the children of this frame (and, in turn, their children). |
|
337 * |
|
338 * Second half of Restyle(). |
|
339 */ |
|
340 void RestyleChildren(nsRestyleHint aChildRestyleHint); |
|
341 |
|
342 /** |
|
343 * Helper for RestyleSelf(). |
|
344 */ |
|
345 void CaptureChange(nsStyleContext* aOldContext, |
|
346 nsStyleContext* aNewContext, |
|
347 nsChangeHint aChangeToAssume); |
|
348 |
|
349 /** |
|
350 * Helpers for RestyleChildren(). |
|
351 */ |
|
352 void RestyleUndisplayedChildren(nsRestyleHint aChildRestyleHint); |
|
353 void RestyleBeforePseudo(); |
|
354 void RestyleAfterPseudo(nsIFrame* aFrame); |
|
355 void RestyleContentChildren(nsIFrame* aParent, |
|
356 nsRestyleHint aChildRestyleHint); |
|
357 void InitializeAccessibilityNotifications(); |
|
358 void SendAccessibilityNotifications(); |
|
359 |
|
360 enum DesiredA11yNotifications { |
|
361 eSkipNotifications, |
|
362 eSendAllNotifications, |
|
363 eNotifyIfShown |
|
364 }; |
|
365 |
|
366 enum A11yNotificationType { |
|
367 eDontNotify, |
|
368 eNotifyShown, |
|
369 eNotifyHidden |
|
370 }; |
|
371 |
|
372 private: |
|
373 nsPresContext* const mPresContext; |
|
374 nsIFrame* const mFrame; |
|
375 nsIContent* const mParentContent; |
|
376 // |mContent| is the node that we used for rule matching of |
|
377 // normal elements (not pseudo-elements) and for which we generate |
|
378 // framechange hints if we need them. |
|
379 nsIContent* const mContent; |
|
380 nsStyleChangeList* const mChangeList; |
|
381 // We have already generated change list entries for hints listed in |
|
382 // mHintsHandled (initially it's those handled by ancestors, but by |
|
383 // the end of Restyle it is those handled for this frame as well). We |
|
384 // need to generate a new change list entry for the frame when its |
|
385 // style comparision returns a hint other than one of these hints. |
|
386 nsChangeHint mHintsHandled; |
|
387 // See nsStyleContext::CalcStyleDifference |
|
388 nsChangeHint mParentFrameHintsNotHandledForDescendants; |
|
389 nsChangeHint mHintsNotHandledForDescendants; |
|
390 RestyleTracker& mRestyleTracker; |
|
391 TreeMatchContext& mTreeMatchContext; |
|
392 nsIFrame* mResolvedChild; // child that provides our parent style context |
|
393 |
|
394 #ifdef ACCESSIBILITY |
|
395 const DesiredA11yNotifications mDesiredA11yNotifications; |
|
396 DesiredA11yNotifications mKidsDesiredA11yNotifications; |
|
397 A11yNotificationType mOurA11yNotification; |
|
398 nsTArray<nsIContent*>& mVisibleKidsOfHiddenElement; |
|
399 bool mWasFrameVisible; |
|
400 #endif |
|
401 }; |
|
402 |
|
403 } // namespace mozilla |
|
404 |
|
405 #endif /* mozilla_RestyleManager_h */ |