Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef mozilla_RestyleManager_h
8 : #define mozilla_RestyleManager_h
9 :
10 : #include "mozilla/OverflowChangedTracker.h"
11 : #include "nsChangeHint.h"
12 : #include "nsPresContext.h"
13 :
14 : class nsCString;
15 : class nsCSSFrameConstructor;
16 : class nsStyleChangeList;
17 :
18 : namespace mozilla {
19 :
20 : class EventStates;
21 : class GeckoRestyleManager;
22 : class ServoRestyleManager;
23 :
24 : namespace dom {
25 : class Element;
26 : }
27 :
28 : /**
29 : * Class for sharing data and logic common to both GeckoRestyleManager and
30 : * ServoRestyleManager.
31 : */
32 : class RestyleManager
33 : {
34 : public:
35 : typedef mozilla::dom::Element Element;
36 :
37 32 : NS_INLINE_DECL_REFCOUNTING(mozilla::RestyleManager)
38 :
39 : // Get an integer that increments every time we process pending restyles.
40 : // The value is never 0.
41 0 : uint32_t GetRestyleGeneration() const { return mRestyleGeneration; }
42 : // Unlike GetRestyleGeneration, which means the actual restyling count,
43 : // GetUndisplayedRestyleGeneration represents any possible DOM changes that
44 : // can cause restyling. This is needed for getComputedStyle to work with
45 : // non-styled (e.g. display: none) elements.
46 4 : uint32_t GetUndisplayedRestyleGeneration() const {
47 4 : return mUndisplayedRestyleGeneration;
48 : }
49 :
50 : // Get an integer that increments every time there is a style change
51 : // as a result of a change to the :hover content state.
52 0 : uint32_t GetHoverGeneration() const { return mHoverGeneration; }
53 :
54 4 : void Disconnect() { mPresContext = nullptr; }
55 :
56 : static nsCString RestyleHintToString(nsRestyleHint aHint);
57 :
58 : #ifdef DEBUG
59 : static nsCString ChangeHintToString(nsChangeHint aHint);
60 :
61 : /**
62 : * DEBUG ONLY method to verify integrity of style tree versus frame tree
63 : */
64 : void DebugVerifyStyleTree(nsIFrame* aFrame);
65 : #endif
66 :
67 25 : void FlushOverflowChangedTracker() {
68 25 : mOverflowChangedTracker.Flush();
69 25 : }
70 :
71 : // Should be called when a frame is going to be destroyed and
72 : // WillDestroyFrameTree hasn't been called yet.
73 126 : void NotifyDestroyingFrame(nsIFrame* aFrame) {
74 126 : mOverflowChangedTracker.RemoveFrame(aFrame);
75 : // If ProcessRestyledFrames is tracking frames which have been
76 : // destroyed (to avoid re-visiting them), add this one to its set.
77 126 : if (mDestroyedFrames) {
78 121 : mDestroyedFrames->PutEntry(aFrame);
79 : }
80 126 : }
81 :
82 : // Note: It's the caller's responsibility to make sure to wrap a
83 : // ProcessRestyledFrames call in a view update batch and a script blocker.
84 : // This function does not call ProcessAttachedQueue() on the binding manager.
85 : // If the caller wants that to happen synchronously, it needs to handle that
86 : // itself.
87 : void ProcessRestyledFrames(nsStyleChangeList& aChangeList);
88 :
89 80 : bool IsInStyleRefresh() const { return mInStyleRefresh; }
90 :
91 : // AnimationsWithDestroyedFrame is used to stop animations and transitions
92 : // on elements that have no frame at the end of the restyling process.
93 : // It only lives during the restyling process.
94 25 : class MOZ_STACK_CLASS AnimationsWithDestroyedFrame final {
95 : public:
96 : // Construct a AnimationsWithDestroyedFrame object. The caller must
97 : // ensure that aRestyleManager lives at least as long as the
98 : // object. (This is generally easy since the caller is typically a
99 : // method of RestyleManager.)
100 : explicit AnimationsWithDestroyedFrame(RestyleManager* aRestyleManager);
101 :
102 : // This method takes the content node for the generated content for
103 : // animation/transition on ::before and ::after, rather than the
104 : // content node for the real element.
105 2 : void Put(nsIContent* aContent, nsStyleContext* aStyleContext) {
106 2 : MOZ_ASSERT(aContent);
107 2 : CSSPseudoElementType pseudoType = aStyleContext->GetPseudoType();
108 2 : if (pseudoType == CSSPseudoElementType::NotPseudo) {
109 2 : mContents.AppendElement(aContent);
110 0 : } else if (pseudoType == CSSPseudoElementType::before) {
111 0 : MOZ_ASSERT(aContent->NodeInfo()->NameAtom() ==
112 : nsGkAtoms::mozgeneratedcontentbefore);
113 0 : mBeforeContents.AppendElement(aContent->GetParent());
114 0 : } else if (pseudoType == CSSPseudoElementType::after) {
115 0 : MOZ_ASSERT(aContent->NodeInfo()->NameAtom() ==
116 : nsGkAtoms::mozgeneratedcontentafter);
117 0 : mAfterContents.AppendElement(aContent->GetParent());
118 : }
119 2 : }
120 :
121 : void StopAnimationsForElementsWithoutFrames();
122 :
123 : private:
124 : void StopAnimationsWithoutFrame(nsTArray<RefPtr<nsIContent>>& aArray,
125 : CSSPseudoElementType aPseudoType);
126 :
127 : RestyleManager* mRestyleManager;
128 : AutoRestore<AnimationsWithDestroyedFrame*> mRestorePointer;
129 :
130 : // Below three arrays might include elements that have already had their
131 : // animations or transitions stopped.
132 : //
133 : // mBeforeContents and mAfterContents hold the real element rather than
134 : // the content node for the generated content (which might change during
135 : // a reframe)
136 : nsTArray<RefPtr<nsIContent>> mContents;
137 : nsTArray<RefPtr<nsIContent>> mBeforeContents;
138 : nsTArray<RefPtr<nsIContent>> mAfterContents;
139 : };
140 :
141 : /**
142 : * Return the current AnimationsWithDestroyedFrame struct, or null if we're
143 : * not currently in a restyling operation.
144 : */
145 2 : AnimationsWithDestroyedFrame* GetAnimationsWithDestroyedFrame() {
146 2 : return mAnimationsWithDestroyedFrame;
147 : }
148 :
149 : void ContentInserted(nsINode* aContainer, nsIContent* aChild);
150 : void ContentAppended(nsIContent* aContainer, nsIContent* aFirstNewContent);
151 :
152 : // This would be have the same logic as RestyleForInsertOrChange if we got the
153 : // notification before the removal. However, we get it after, so we need the
154 : // following sibling in addition to the old child. |aContainer| must be
155 : // non-null; when the container is null, no work is needed. aFollowingSibling
156 : // is the sibling that used to come after aOldChild before the removal.
157 : void ContentRemoved(nsINode* aContainer,
158 : nsIContent* aOldChild,
159 : nsIContent* aFollowingSibling);
160 :
161 : // Restyling for a ContentInserted (notification after insertion) or
162 : // for a CharacterDataChanged. |aContainer| must be non-null; when
163 : // the container is null, no work is needed.
164 : void RestyleForInsertOrChange(nsINode* aContainer, nsIContent* aChild);
165 :
166 : // Restyling for a ContentAppended (notification after insertion) or
167 : // for a CharacterDataChanged. |aContainer| must be non-null; when
168 : // the container is null, no work is needed.
169 : void RestyleForAppend(nsIContent* aContainer, nsIContent* aFirstNewContent);
170 :
171 25939 : MOZ_DECL_STYLO_METHODS(GeckoRestyleManager, ServoRestyleManager)
172 :
173 : inline void PostRestyleEvent(dom::Element* aElement,
174 : nsRestyleHint aRestyleHint,
175 : nsChangeHint aMinChangeHint);
176 : inline void RebuildAllStyleData(nsChangeHint aExtraHint,
177 : nsRestyleHint aRestyleHint);
178 : inline void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
179 : nsRestyleHint aRestyleHint);
180 : inline void ProcessPendingRestyles();
181 : inline void ContentStateChanged(nsIContent* aContent,
182 : EventStates aStateMask);
183 : inline void AttributeWillChange(dom::Element* aElement,
184 : int32_t aNameSpaceID,
185 : nsIAtom* aAttribute,
186 : int32_t aModType,
187 : const nsAttrValue* aNewValue);
188 : inline void AttributeChanged(dom::Element* aElement,
189 : int32_t aNameSpaceID,
190 : nsIAtom* aAttribute,
191 : int32_t aModType,
192 : const nsAttrValue* aOldValue);
193 : inline nsresult ReparentStyleContext(nsIFrame* aFrame);
194 :
195 : inline void UpdateOnlyAnimationStyles();
196 :
197 : // Get a counter that increments on every style change, that we use to
198 : // track whether off-main-thread animations are up-to-date.
199 28 : uint64_t GetAnimationGeneration() const { return mAnimationGeneration; }
200 :
201 : static uint64_t GetAnimationGenerationForFrame(nsIFrame* aFrame);
202 :
203 : // Update the animation generation count to mark that animation state
204 : // has changed.
205 : //
206 : // This is normally performed automatically by ProcessPendingRestyles
207 : // but it is also called when we have out-of-band changes to animations
208 : // such as changes made through the Web Animations API.
209 : void IncrementAnimationGeneration();
210 :
211 : static void AddLayerChangesForAnimation(nsIFrame* aFrame,
212 : nsIContent* aContent,
213 : nsStyleChangeList&
214 : aChangeListToProcess);
215 :
216 : protected:
217 : RestyleManager(StyleBackendType aType, nsPresContext* aPresContext);
218 :
219 4 : virtual ~RestyleManager()
220 8 : {
221 4 : MOZ_ASSERT(!mAnimationsWithDestroyedFrame,
222 : "leaving dangling pointers from AnimationsWithDestroyedFrame");
223 4 : }
224 :
225 : void RestyleForEmptyChange(Element* aContainer);
226 :
227 : void ContentStateChangedInternal(Element* aElement,
228 : EventStates aStateMask,
229 : nsChangeHint* aOutChangeHint,
230 : nsRestyleHint* aOutRestyleHint);
231 :
232 767 : bool IsDisconnected() { return mPresContext == nullptr; }
233 :
234 0 : void IncrementHoverGeneration() {
235 0 : ++mHoverGeneration;
236 0 : }
237 :
238 25 : void IncrementRestyleGeneration() {
239 25 : if (++mRestyleGeneration == 0) {
240 : // Keep mRestyleGeneration from being 0, since that's what
241 : // nsPresContext::GetRestyleGeneration returns when it no
242 : // longer has a RestyleManager.
243 0 : ++mRestyleGeneration;
244 : }
245 25 : IncrementUndisplayedRestyleGeneration();
246 25 : }
247 :
248 25 : void IncrementUndisplayedRestyleGeneration() {
249 25 : if (++mUndisplayedRestyleGeneration == 0) {
250 : // Ensure mUndisplayedRestyleGeneration > 0, for the same reason as
251 : // IncrementRestyleGeneration.
252 0 : ++mUndisplayedRestyleGeneration;
253 : }
254 25 : }
255 :
256 7178 : nsPresContext* PresContext() const {
257 7178 : MOZ_ASSERT(mPresContext);
258 7178 : return mPresContext;
259 : }
260 :
261 50 : nsCSSFrameConstructor* FrameConstructor() const {
262 50 : return PresContext()->FrameConstructor();
263 : }
264 :
265 : private:
266 : nsPresContext* mPresContext; // weak, can be null after Disconnect().
267 : uint32_t mRestyleGeneration;
268 : uint32_t mUndisplayedRestyleGeneration;
269 : uint32_t mHoverGeneration;
270 :
271 : // Used to keep track of frames that have been destroyed during
272 : // ProcessRestyledFrames, so we don't try to touch them again even if
273 : // they're referenced again later in the changelist.
274 : mozilla::UniquePtr<nsTHashtable<nsPtrHashKey<const nsIFrame>>> mDestroyedFrames;
275 :
276 : protected:
277 : const StyleBackendType mType;
278 :
279 : // True if we're in the middle of a nsRefreshDriver refresh
280 : bool mInStyleRefresh;
281 :
282 : // The total number of animation flushes by this frame constructor.
283 : // Used to keep the layer and animation manager in sync.
284 : uint64_t mAnimationGeneration;
285 :
286 : OverflowChangedTracker mOverflowChangedTracker;
287 :
288 : /**
289 : * These are protected static methods that help with the change hint
290 : * processing bits of the restyle managers.
291 : */
292 : static nsIFrame*
293 : GetNearestAncestorFrame(nsIContent* aContent);
294 :
295 : static nsIFrame*
296 : GetNextBlockInInlineSibling(nsIFrame* aFrame);
297 :
298 : /**
299 : * Get the next continuation or similar ib-split sibling (assuming
300 : * block/inline alternation), conditionally on it having the same style.
301 : *
302 : * Since this is used when deciding to copy the new style context, it
303 : * takes as an argument the old style context to check if the style is
304 : * the same. When it is used in other contexts (i.e., where the next
305 : * continuation would already have the new style context), the current
306 : * style context should be passed.
307 : */
308 : static nsIFrame*
309 : GetNextContinuationWithSameStyle(nsIFrame* aFrame,
310 : nsStyleContext* aOldStyleContext,
311 : bool* aHaveMoreContinuations = nullptr);
312 :
313 : AnimationsWithDestroyedFrame* mAnimationsWithDestroyedFrame = nullptr;
314 :
315 : friend class mozilla::GeckoRestyleManager;
316 : friend class mozilla::ServoRestyleManager;
317 : };
318 :
319 : } // namespace mozilla
320 :
321 : #endif
|