RestyleManager.h 22.9 KB
Newer Older
1
2
3
4
5
6
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

7
8
#ifndef mozilla_RestyleManager_h
#define mozilla_RestyleManager_h
9

10
#include "mozilla/AutoRestore.h"
11
12
#include "mozilla/EventStates.h"
#include "mozilla/Maybe.h"
13
#include "mozilla/OverflowChangedTracker.h"
14
15
#include "mozilla/ServoElementSnapshot.h"
#include "mozilla/ServoElementSnapshotTable.h"
16
#include "nsChangeHint.h"
17
#include "nsPresContext.h"
18
#include "nsPresContextInlines.h"  // XXX Shouldn't be included by header though
19
#include "nsStringFwd.h"
20

21
class nsAttrValue;
22
class nsCSSFrameConstructor;
23
24
25
26
class nsAtom;
class nsIContent;
class nsIFrame;
class nsStyleChangeList;
27
28
class nsStyleChangeList;

29
30
namespace mozilla {

31
class EventStates;
32
class ServoStyleSet;
33
34
35
36

namespace dom {
class Element;
}
37
38

/**
39
40
41
 * A stack class used to pass some common restyle state in a slightly more
 * comfortable way than a bunch of individual arguments, and that also checks
 * that the change hint used for optimization is correctly used in debug mode.
42
 */
43
44
class ServoRestyleState {
 public:
45
46
47
48
  ServoRestyleState(
      ServoStyleSet& aStyleSet, nsStyleChangeList& aChangeList,
      nsTArray<nsIFrame*>& aPendingWrapperRestyles,
      nsTArray<RefPtr<dom::Element>>& aPendingScrollAnchorSuppressions)
49
50
51
      : mStyleSet(aStyleSet),
        mChangeList(aChangeList),
        mPendingWrapperRestyles(aPendingWrapperRestyles),
52
        mPendingScrollAnchorSuppressions(aPendingScrollAnchorSuppressions),
53
54
        mPendingWrapperRestyleOffset(aPendingWrapperRestyles.Length()),
        mChangesHandled(nsChangeHint(0))
55
#ifdef DEBUG
56
57
58
59
60
61
62
63
64
65
66
        // If !mOwner, then we wouldn't have processed our wrapper restyles,
        // because we only process those when handling an element with a frame.
        // But that's OK, because if we started our traversal at an element with
        // no frame (e.g. it's display:contents), that means the wrapper frames
        // in our list actually inherit from one of its ancestors, not from it,
        // and hence not restyling them is OK.
        ,
        mAssertWrapperRestyleLength(false)
#endif  // DEBUG
  {
  }
67
68
69
70
71

  // We shouldn't assume that changes handled from our parent are handled for
  // our children too if we're out of flow since they aren't necessarily
  // parented in DOM order, and thus a change handled by a DOM ancestor doesn't
  // necessarily mean that it's handled for an ancestor frame.
72
  enum class Type {
73
74
75
76
    InFlow,
    OutOfFlow,
  };

77
78
  ServoRestyleState(const nsIFrame& aOwner, ServoRestyleState& aParentState,
                    nsChangeHint aHintForThisFrame, Type aType,
79
                    bool aAssertWrapperRestyleLength = true)
80
81
82
      : mStyleSet(aParentState.mStyleSet),
        mChangeList(aParentState.mChangeList),
        mPendingWrapperRestyles(aParentState.mPendingWrapperRestyles),
83
84
        mPendingScrollAnchorSuppressions(
            aParentState.mPendingScrollAnchorSuppressions),
85
86
87
88
89
        mPendingWrapperRestyleOffset(
            aParentState.mPendingWrapperRestyles.Length()),
        mChangesHandled(aType == Type::InFlow
                            ? aParentState.mChangesHandled | aHintForThisFrame
                            : aHintForThisFrame)
90
#ifdef DEBUG
91
92
93
        ,
        mOwner(&aOwner),
        mAssertWrapperRestyleLength(aAssertWrapperRestyleLength)
94
95
96
97
98
99
100
101
#endif
  {
    if (aType == Type::InFlow) {
      AssertOwner(aParentState);
    }
  }

  ~ServoRestyleState() {
102
103
104
105
    MOZ_ASSERT(
        !mAssertWrapperRestyleLength ||
            mPendingWrapperRestyles.Length() == mPendingWrapperRestyleOffset,
        "Someone forgot to call ProcessWrapperRestyles!");
106
107
108
109
110
111
112
  }

  nsStyleChangeList& ChangeList() { return mChangeList; }
  ServoStyleSet& StyleSet() { return mStyleSet; }

#ifdef DEBUG
  void AssertOwner(const ServoRestyleState& aParentState) const;
113
  nsChangeHint ChangesHandledFor(const nsIFrame*) const;
114
115
#else
  void AssertOwner(const ServoRestyleState&) const {}
116
  nsChangeHint ChangesHandledFor(const nsIFrame*) const {
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
    return mChangesHandled;
  }
#endif

  // Add a pending wrapper restyle.  We don't have to do anything if the thing
  // being added is already last in the list, but otherwise we do want to add
  // it, in order for ProcessWrapperRestyles to work correctly.
  void AddPendingWrapperRestyle(nsIFrame* aWrapperFrame);

  // Process wrapper restyles for this restyle state.  This should be done
  // before it comes off the stack.
  void ProcessWrapperRestyles(nsIFrame* aParentFrame);

  // Get the table-aware parent for the given child.  This will walk through
  // outer table and cellcontent frames.
  static nsIFrame* TableAwareParentFor(const nsIFrame* aChild);

134
135
136
137
138
139
140
141
142
143
  // When the value of the position property changes such as we stop or start
  // being absolutely or fixed positioned, we need to suppress scroll anchoring
  // adjustments to avoid breaking websites.
  //
  // We do need to process all this once we're done with all our reframes,
  // to handle correctly the cases where we reconstruct an ancestor, like when
  // you reframe an ib-split (see bug 1559627 for example).
  //
  // This doesn't handle nested reframes. We'd need to rework quite some code to
  // do that, and so far it doesn't seem to be a problem in practice.
144
145
  void AddPendingScrollAnchorSuppression(dom::Element* aElement) {
    mPendingScrollAnchorSuppressions.AppendElement(aElement);
146
147
  }

148
 private:
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
  // Process a wrapper restyle at the given index, and restyles for any
  // wrappers nested in it.  Returns the number of entries from
  // mPendingWrapperRestyles that we processed.  The return value is always at
  // least 1.
  size_t ProcessMaybeNestedWrapperRestyle(nsIFrame* aParent, size_t aIndex);

  ServoStyleSet& mStyleSet;
  nsStyleChangeList& mChangeList;

  // A list of pending wrapper restyles.  Anonymous box wrapper frames that need
  // restyling are added to this list when their non-anonymous kids are
  // restyled.  This avoids us having to do linear searches along the frame tree
  // for these anonymous boxes.  The problem then becomes that we can have
  // multiple kids all with the same anonymous parent, and we don't want to
  // restyle it more than once.  We use mPendingWrapperRestyles to track which
  // anonymous wrapper boxes we've requested be restyled and which of them have
  // already been restyled.  We use a single array propagated through
  // ServoRestyleStates by reference, because in a situation like this:
  //
  //  <div style="display: table"><span></span></div>
  //
  // We have multiple wrappers to restyle (cell, row, table-row-group) and we
  // want to add them in to the list all at once but restyle them using
  // different ServoRestyleStates with different owners.  When this situation
  // occurs, the relevant frames will be placed in the array with ancestors
  // before descendants.
  nsTArray<nsIFrame*>& mPendingWrapperRestyles;

177
  nsTArray<RefPtr<dom::Element>>& mPendingScrollAnchorSuppressions;
178

179
180
181
182
183
184
185
186
187
188
189
190
191
  // Since we're given a possibly-nonempty mPendingWrapperRestyles to start
  // with, we need to keep track of where the part of it we're responsible for
  // starts.
  size_t mPendingWrapperRestyleOffset;

  const nsChangeHint mChangesHandled;

  // We track the "owner" frame of this restyle state, that is, the frame that
  // generated the last change that is stored in mChangesHandled, in order to
  // verify that we only use mChangesHandled for actual descendants of that
  // frame (given DOM order isn't always frame order, and that there are a few
  // special cases for stuff like wrapper frames, ::backdrop, and so on).
#ifdef DEBUG
192
  const nsIFrame* mOwner{nullptr};
193
194
195
196
197
198
#endif

  // Whether we should assert in our destructor that we've processed all of the
  // relevant wrapper restyles.
#ifdef DEBUG
  const bool mAssertWrapperRestyleLength;
199
#endif  // DEBUG
200
201
202
203
};

enum class ServoPostTraversalFlags : uint32_t;

204
class RestyleManager {
205
206
  friend class ServoStyleSet;

207
 public:
208
  typedef ServoElementSnapshotTable SnapshotTable;
209
210
211
212
  typedef mozilla::dom::Element Element;

  // Get an integer that increments every time we process pending restyles.
  // The value is never 0.
213
  uint64_t GetRestyleGeneration() const { return mRestyleGeneration; }
214
215
216
217
  // Unlike GetRestyleGeneration, which means the actual restyling count,
  // GetUndisplayedRestyleGeneration represents any possible DOM changes that
  // can cause restyling. This is needed for getComputedStyle to work with
  // non-styled (e.g. display: none) elements.
218
  uint64_t GetUndisplayedRestyleGeneration() const {
219
220
    return mUndisplayedRestyleGeneration;
  }
221
222
223

  void Disconnect() { mPresContext = nullptr; }

224
  ~RestyleManager() {
225
226
227
228
229
    MOZ_ASSERT(!mAnimationsWithDestroyedFrame,
               "leaving dangling pointers from AnimationsWithDestroyedFrame");
    MOZ_ASSERT(!mReentrantChanges);
  }

230
#ifdef DEBUG
231
232
  static nsCString ChangeHintToString(nsChangeHint aHint);

233
234
235
  /**
   * DEBUG ONLY method to verify integrity of style tree versus frame tree
   */
236
  void DebugVerifyStyleTree(nsIFrame* aFrame);
237
238
#endif

239
  void FlushOverflowChangedTracker() { mOverflowChangedTracker.Flush(); }
240
241
242
243
244

  // Should be called when a frame is going to be destroyed and
  // WillDestroyFrameTree hasn't been called yet.
  void NotifyDestroyingFrame(nsIFrame* aFrame) {
    mOverflowChangedTracker.RemoveFrame(aFrame);
245
246
247
248
249
    // If ProcessRestyledFrames is tracking frames which have been
    // destroyed (to avoid re-visiting them), add this one to its set.
    if (mDestroyedFrames) {
      mDestroyedFrames->PutEntry(aFrame);
    }
250
251
  }

252
253
254
255
256
  // Note: It's the caller's responsibility to make sure to wrap a
  // ProcessRestyledFrames call in a view update batch and a script blocker.
  // This function does not call ProcessAttachedQueue() on the binding manager.
  // If the caller wants that to happen synchronously, it needs to handle that
  // itself.
257
  void ProcessRestyledFrames(nsStyleChangeList& aChangeList);
258

259
260
  bool IsInStyleRefresh() const { return mInStyleRefresh; }

261
262
263
264
  // AnimationsWithDestroyedFrame is used to stop animations and transitions
  // on elements that have no frame at the end of the restyling process.
  // It only lives during the restyling process.
  class MOZ_STACK_CLASS AnimationsWithDestroyedFrame final {
265
   public:
266
267
268
269
    // Construct a AnimationsWithDestroyedFrame object.  The caller must
    // ensure that aRestyleManager lives at least as long as the
    // object.  (This is generally easy since the caller is typically a
    // method of RestyleManager.)
270
    explicit AnimationsWithDestroyedFrame(RestyleManager* aRestyleManager);
271
272
273
274

    // This method takes the content node for the generated content for
    // animation/transition on ::before and ::after, rather than the
    // content node for the real element.
275
    void Put(nsIContent* aContent, ComputedStyle* aComputedStyle) {
276
      MOZ_ASSERT(aContent);
277
278
      PseudoStyleType pseudoType = aComputedStyle->GetPseudoType();
      if (pseudoType == PseudoStyleType::NotPseudo) {
279
        mContents.AppendElement(aContent);
280
      } else if (pseudoType == PseudoStyleType::before) {
281
        MOZ_ASSERT(aContent->NodeInfo()->NameAtom() ==
282
                   nsGkAtoms::mozgeneratedcontentbefore);
283
        mBeforeContents.AppendElement(aContent->GetParent());
284
      } else if (pseudoType == PseudoStyleType::after) {
285
        MOZ_ASSERT(aContent->NodeInfo()->NameAtom() ==
286
                   nsGkAtoms::mozgeneratedcontentafter);
287
        mAfterContents.AppendElement(aContent->GetParent());
288
289
290
291
      } else if (pseudoType == PseudoStyleType::marker) {
        MOZ_ASSERT(aContent->NodeInfo()->NameAtom() ==
                   nsGkAtoms::mozgeneratedcontentmarker);
        mMarkerContents.AppendElement(aContent->GetParent());
292
293
294
295
296
      }
    }

    void StopAnimationsForElementsWithoutFrames();

297
   private:
298
    void StopAnimationsWithoutFrame(nsTArray<RefPtr<nsIContent>>& aArray,
299
                                    PseudoStyleType aPseudoType);
300

301
    RestyleManager* mRestyleManager;
302
303
304
305
306
    AutoRestore<AnimationsWithDestroyedFrame*> mRestorePointer;

    // Below three arrays might include elements that have already had their
    // animations or transitions stopped.
    //
307
308
309
    // mBeforeContents, mAfterContents and mMarkerContents hold the real element
    // rather than the content node for the generated content (which might
    // change during a reframe)
310
311
312
    nsTArray<RefPtr<nsIContent>> mContents;
    nsTArray<RefPtr<nsIContent>> mBeforeContents;
    nsTArray<RefPtr<nsIContent>> mAfterContents;
313
    nsTArray<RefPtr<nsIContent>> mMarkerContents;
314
315
316
317
318
319
320
321
322
323
  };

  /**
   * Return the current AnimationsWithDestroyedFrame struct, or null if we're
   * not currently in a restyling operation.
   */
  AnimationsWithDestroyedFrame* GetAnimationsWithDestroyedFrame() {
    return mAnimationsWithDestroyedFrame;
  }

324
325
  void ContentInserted(nsIContent* aChild);
  void ContentAppended(nsIContent* aFirstNewContent);
326

327
328
  // This would be have the same logic as RestyleForInsertOrChange if we got the
  // notification before the removal.  However, we get it after, so we need the
329
330
331
332
333
  // following sibling in addition to the old child.
  //
  // aFollowingSibling is the sibling that used to come after aOldChild before
  // the removal.
  void ContentRemoved(nsIContent* aOldChild, nsIContent* aFollowingSibling);
334
335

  // Restyling for a ContentInserted (notification after insertion) or
336
337
  // for some CharacterDataChanged.
  void RestyleForInsertOrChange(nsIContent* aChild);
338

339
340
341
  // Restyle for a CharacterDataChanged notification. In practice this can only
  // affect :empty / :-moz-only-whitespace / :-moz-first-node / :-moz-last-node.
  void CharacterDataChanged(nsIContent*, const CharacterDataChangeInfo&);
342

343
  void PostRestyleEvent(dom::Element*, RestyleHint,
344
                        nsChangeHint aMinChangeHint);
345
346
347
348
349
350
351
352
353
354

  /**
   * Posts restyle hints for animations.
   * This is only called for the second traversal for CSS animations during
   * updating CSS animations in a SequentialTask.
   * This function does neither register a refresh observer nor flag that a
   * style flush is needed since this function is supposed to be called during
   * restyling process and this restyle event will be processed in the second
   * traversal of the same restyling process.
   */
355
  void PostRestyleEventForAnimations(dom::Element*, PseudoStyleType,
356
                                     RestyleHint);
357

358
  void NextRestyleIsForCSSRuleChanges() { mRestyleForCSSRuleChanges = true; }
359

360
  void RebuildAllStyleData(nsChangeHint aExtraHint, RestyleHint);
361
362
363
364
365

  void ProcessPendingRestyles();
  void ProcessAllPendingAttributeAndStateInvalidations();

  void ContentStateChanged(nsIContent* aContent, EventStates aStateMask);
366
  void AttributeWillChange(Element* aElement, int32_t aNameSpaceID,
367
                           nsAtom* aAttribute, int32_t aModType);
368
  void ClassAttributeWillBeChangedBySMIL(dom::Element* aElement);
369
370
  void AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID,
                        nsAtom* aAttribute, int32_t aModType,
371
372
373
                        const nsAttrValue* aOldValue);

  // This is only used to reparent things when moving them in/out of the
374
375
  // ::first-line.
  void ReparentComputedStyleForFirstLine(nsIFrame*);
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390

  /**
   * Performs a Servo animation-only traversal to compute style for all nodes
   * with the animation-only dirty bit in the document.
   *
   * This processes just the traversal for animation-only restyles and skips the
   * normal traversal for other restyles unrelated to animations.
   * This is used to bring throttled animations up-to-date such as when we need
   * to get correct position for transform animations that are throttled because
   * they are running on the compositor.
   *
   * This will traverse all of the document's style roots (that is, its document
   * element, and the roots of the document-level native anonymous content).
   */
  void UpdateOnlyAnimationStyles();
391

392
393
394
395
  // Get a counter that increments on every style change, that we use to
  // track whether off-main-thread animations are up-to-date.
  uint64_t GetAnimationGeneration() const { return mAnimationGeneration; }

396
397
398
  // Typically only style frames have animations associated with them so this
  // will likely return zero for anything that is not a style frame.
  static uint64_t GetAnimationGenerationForFrame(nsIFrame* aStyleFrame);
399
400
401
402
403
404
405
406
407

  // Update the animation generation count to mark that animation state
  // has changed.
  //
  // This is normally performed automatically by ProcessPendingRestyles
  // but it is also called when we have out-of-band changes to animations
  // such as changes made through the Web Animations API.
  void IncrementAnimationGeneration();

408
409
410
411
412
413
414
415
416
417
418
  // Apply change hints for animations on the compositor.
  //
  // There are some cases where we forcibly apply change hints for animations
  // even if there is no change hint produced in order to synchronize with
  // animations running on the compositor.
  //
  // For example:
  //
  // a) Pausing animations via the Web Animations API
  // b) When the style before sending the animation to the compositor exactly
  // the same as the current style
419
  static void AddLayerChangesForAnimation(
420
421
      nsIFrame* aStyleFrame, nsIFrame* aPrimaryFrame, Element* aElement,
      nsChangeHint aHintForThisFrame, nsStyleChangeList& aChangeListToProcess);
422

423
424
425
426
427
428
429
430
431
432
433
434
435
  /**
   * Whether to clear all the style data (including the element itself), or just
   * the descendants' data.
   */
  enum class IncludeRoot {
    Yes,
    No,
  };

  /**
   * Clears the ServoElementData and HasDirtyDescendants from all elements
   * in the subtree rooted at aElement.
   */
436
437
  static void ClearServoDataFromSubtree(Element*,
                                        IncludeRoot = IncludeRoot::Yes);
438
439
440
441
442
443
444

  /**
   * Clears HasDirtyDescendants and RestyleData from all elements in the
   * subtree rooted at aElement.
   */
  static void ClearRestyleStateFromSubtree(Element* aElement);

445
  explicit RestyleManager(nsPresContext* aPresContext);
446

447
 protected:
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
  /**
   * Reparent the descendants of aFrame.  This is used by ReparentComputedStyle
   * and shouldn't be called by anyone else.  aProviderChild, if non-null, is a
   * child that was the style parent for aFrame and hence shouldn't be
   * reparented.
   */
  void ReparentFrameDescendants(nsIFrame* aFrame, nsIFrame* aProviderChild,
                                ServoStyleSet& aStyleSet);

  /**
   * Performs post-Servo-traversal processing on this element and its
   * descendants.
   *
   * Returns whether any style did actually change. There may be cases where we
   * didn't need to change any style after all, for example, when a content
   * attribute changes that happens not to have any effect on the style of that
   * element or any descendant or sibling.
   */
466
  bool ProcessPostTraversal(Element* aElement, ServoRestyleState& aRestyleState,
467
468
469
470
471
472
473
474
475
476
                            ServoPostTraversalFlags aFlags);

  struct TextPostTraversalState;
  bool ProcessPostTraversalForText(nsIContent* aTextNode,
                                   TextPostTraversalState& aState,
                                   ServoRestyleState& aRestyleState,
                                   ServoPostTraversalFlags aFlags);

  ServoStyleSet* StyleSet() const { return PresContext()->StyleSet(); }

477
  void RestyleForEmptyChange(Element* aContainer);
478
479
  void MaybeRestyleForEdgeChildChange(Element* aContainer,
                                      nsIContent* aChangedChild);
480

481
  bool IsDisconnected() const { return !mPresContext; }
482
483
484
485
486
487
488
489

  void IncrementRestyleGeneration() {
    if (++mRestyleGeneration == 0) {
      // Keep mRestyleGeneration from being 0, since that's what
      // nsPresContext::GetRestyleGeneration returns when it no
      // longer has a RestyleManager.
      ++mRestyleGeneration;
    }
490
491
492
493
494
495
496
497
498
    IncrementUndisplayedRestyleGeneration();
  }

  void IncrementUndisplayedRestyleGeneration() {
    if (++mUndisplayedRestyleGeneration == 0) {
      // Ensure mUndisplayedRestyleGeneration > 0, for the same reason as
      // IncrementRestyleGeneration.
      ++mUndisplayedRestyleGeneration;
    }
499
500
501
502
503
504
505
506
507
508
509
  }

  nsPresContext* PresContext() const {
    MOZ_ASSERT(mPresContext);
    return mPresContext;
  }

  nsCSSFrameConstructor* FrameConstructor() const {
    return PresContext()->FrameConstructor();
  }

510
511
 private:
  nsPresContext* mPresContext;  // weak, can be null after Disconnect().
512
513
  uint64_t mRestyleGeneration;
  uint64_t mUndisplayedRestyleGeneration;
514

515
516
517
  // Used to keep track of frames that have been destroyed during
  // ProcessRestyledFrames, so we don't try to touch them again even if
  // they're referenced again later in the changelist.
518
519
  mozilla::UniquePtr<nsTHashtable<nsPtrHashKey<const nsIFrame>>>
      mDestroyedFrames;
520

521
 protected:
522
523
524
  // True if we're in the middle of a nsRefreshDriver refresh
  bool mInStyleRefresh;

525
526
527
528
  // The total number of animation flushes by this frame constructor.
  // Used to keep the layer and animation manager in sync.
  uint64_t mAnimationGeneration;

529
530
  OverflowChangedTracker mOverflowChangedTracker;

531
  AnimationsWithDestroyedFrame* mAnimationsWithDestroyedFrame = nullptr;
532

533
534
  const SnapshotTable& Snapshots() const { return mSnapshots; }
  void ClearSnapshots();
535
  ServoElementSnapshot& SnapshotFor(Element&);
536
  void TakeSnapshotForAttributeChange(Element&, int32_t aNameSpaceID,
537
538
539
540
                                      nsAtom* aAttribute);

  void DoProcessPendingRestyles(ServoTraversalFlags aFlags);

541
542
543
544
  // Function to do the actual (recursive) work of
  // ReparentComputedStyleForFirstLine, once we have asserted the invariants
  // that only hold on the initial call.
  void DoReparentComputedStyleForFirstLine(nsIFrame*, ServoStyleSet&);
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576

  // We use a separate data structure from nsStyleChangeList because we need a
  // frame to create nsStyleChangeList entries, and the primary frame may not be
  // attached yet.
  struct ReentrantChange {
    nsCOMPtr<nsIContent> mContent;
    nsChangeHint mHint;
  };
  typedef AutoTArray<ReentrantChange, 10> ReentrantChangeList;

  // Only non-null while processing change hints. See the comment in
  // ProcessPendingRestyles.
  ReentrantChangeList* mReentrantChanges = nullptr;

  // We use this flag to track if the current restyle contains any non-animation
  // update, which triggers a normal restyle, and so there might be any new
  // transition created later. Therefore, if this flag is true, we need to
  // increase mAnimationGeneration before creating new transitions, so their
  // creation sequence will be correct.
  bool mHaveNonAnimationRestyles = false;

  // Set to true when posting restyle events triggered by CSS rule changes.
  // This flag is cleared once ProcessPendingRestyles has completed.
  // When we process a traversal all descendants elements of the document
  // triggered by CSS rule changes, we will need to update all elements with
  // CSS animations.  We propagate TraversalRestyleBehavior::ForCSSRuleChanges
  // to traversal function if this flag is set.
  bool mRestyleForCSSRuleChanges = false;

  // A hashtable with the elements that have changed state or attributes, in
  // order to calculate restyle hints during the traversal.
  SnapshotTable mSnapshots;
577
578
};

579
}  // namespace mozilla
580
581

#endif