Blob Blame History Raw
/* -*- 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/. */

/**
 * Code responsible for managing style changes: tracking what style
 * changes need to happen, scheduling them, and doing them.
 */

#ifndef mozilla_GeckoRestyleManager_h
#define mozilla_GeckoRestyleManager_h

#include "mozilla/RestyleLogging.h"
#include "mozilla/RestyleManager.h"
#include "nsISupportsImpl.h"
#include "nsChangeHint.h"
#include "RestyleTracker.h"
#include "nsPresContext.h"
#include "nsRefreshDriver.h"
#include "nsRefPtrHashtable.h"
#include "nsTransitionManager.h"

class nsIFrame;
class nsStyleChangeList;
struct TreeMatchContext;

namespace mozilla {
enum class CSSPseudoElementType : uint8_t;
class EventStates;
struct UndisplayedNode;

namespace dom {
class Element;
}  // namespace dom

class GeckoRestyleManager final : public RestyleManager {
 public:
  typedef RestyleManager base_type;

  friend class RestyleTracker;
  friend class ElementRestyler;

  explicit GeckoRestyleManager(nsPresContext* aPresContext);

 protected:
  ~GeckoRestyleManager() override {
    MOZ_ASSERT(!mReframingStyleContexts,
               "temporary member should be nulled out before destruction");
  }

 public:
  // Forwarded nsIDocumentObserver method, to handle restyling (and
  // passing the notification to the frame).
  void ContentStateChanged(nsIContent* aContent, EventStates aStateMask);

  // Forwarded nsIMutationObserver method, to handle restyling.
  void AttributeWillChange(Element* aElement, int32_t aNameSpaceID,
                           nsAtom* aAttribute, int32_t aModType,
                           const nsAttrValue* aNewValue);
  // Forwarded nsIMutationObserver method, to handle restyling (and
  // passing the notification to the frame).
  void AttributeChanged(Element* aElement, int32_t aNameSpaceID,
                        nsAtom* aAttribute, int32_t aModType,
                        const nsAttrValue* aOldValue);

  // Whether rule matching should skip styles associated with animation
  bool SkipAnimationRules() const { return mSkipAnimationRules; }

  void SetSkipAnimationRules(bool aSkipAnimationRules) {
    mSkipAnimationRules = aSkipAnimationRules;
  }

  /**
   * Reparent the style contexts of this frame subtree.  The parent frame of
   * aFrame must be changed to the new parent before this function is called;
   * the new parent style context will be automatically computed based on the
   * new position in the frame tree.
   *
   * @param aFrame the root of the subtree to reparent.  Must not be null.
   */
  nsresult ReparentStyleContext(nsIFrame* aFrame);

 private:
  /**
   * Reparent the descendants of aFrame.  This is used by ReparentStyleContext
   * 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);

 public:
  void ClearSelectors() { mPendingRestyles.ClearSelectors(); }

  void PostRestyleEventForLazyConstruction() { PostRestyleEventInternal(); }

 private:
  void PostRestyleEventInternal();

  // Used when restyling an element with a frame.
  void ComputeAndProcessStyleChange(nsIFrame* aFrame, nsChangeHint aMinChange,
                                    RestyleTracker& aRestyleTracker,
                                    nsRestyleHint aRestyleHint,
                                    const RestyleHintData& aRestyleHintData);

  // Used when restyling a display:contents element.
  void ComputeAndProcessStyleChange(GeckoStyleContext* aNewContext,
                                    Element* aElement, nsChangeHint aMinChange,
                                    RestyleTracker& aRestyleTracker,
                                    nsRestyleHint aRestyleHint,
                                    const RestyleHintData& aRestyleHintData);

 public:
  /**
   * In order to start CSS transitions on elements that are being
   * reframed, we need to stash their style contexts somewhere during
   * the reframing process.
   *
   * In all cases, the content node in the hash table is the real
   * content node, not the anonymous content node we create for ::before
   * or ::after.  The content node passed to the Get and Put methods is,
   * however, the content node to be associate with the frame's style
   * context.
   */
  typedef nsRefPtrHashtable<nsRefPtrHashKey<nsIContent>, GeckoStyleContext>
      ReframingStyleContextTable;
  class MOZ_STACK_CLASS ReframingStyleContexts final {
   public:
    /**
     * Construct a ReframingStyleContexts 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.)
     */
    explicit ReframingStyleContexts(GeckoRestyleManager* aRestyleManager);
    ~ReframingStyleContexts();

    void Put(nsIContent* aContent, GeckoStyleContext* aStyleContext) {
      MOZ_ASSERT(aContent);
      CSSPseudoElementType pseudoType = aStyleContext->GetPseudoType();
      if (pseudoType == CSSPseudoElementType::NotPseudo) {
        mElementContexts.Put(aContent, aStyleContext);
      } else if (pseudoType == CSSPseudoElementType::before) {
        MOZ_ASSERT(aContent->NodeInfo()->NameAtom() ==
                   nsGkAtoms::mozgeneratedcontentbefore);
        mBeforePseudoContexts.Put(aContent->GetParent(), aStyleContext);
      } else if (pseudoType == CSSPseudoElementType::after) {
        MOZ_ASSERT(aContent->NodeInfo()->NameAtom() ==
                   nsGkAtoms::mozgeneratedcontentafter);
        mAfterPseudoContexts.Put(aContent->GetParent(), aStyleContext);
      }
    }

    GeckoStyleContext* Get(nsIContent* aContent,
                           CSSPseudoElementType aPseudoType) {
      MOZ_ASSERT(aContent);
      if (aPseudoType == CSSPseudoElementType::NotPseudo) {
        return mElementContexts.GetWeak(aContent);
      }
      if (aPseudoType == CSSPseudoElementType::before) {
        MOZ_ASSERT(aContent->NodeInfo()->NameAtom() ==
                   nsGkAtoms::mozgeneratedcontentbefore);
        return mBeforePseudoContexts.GetWeak(aContent->GetParent());
      }
      if (aPseudoType == CSSPseudoElementType::after) {
        MOZ_ASSERT(aContent->NodeInfo()->NameAtom() ==
                   nsGkAtoms::mozgeneratedcontentafter);
        return mAfterPseudoContexts.GetWeak(aContent->GetParent());
      }
      MOZ_ASSERT(false, "unexpected aPseudoType");
      return nullptr;
    }

   private:
    GeckoRestyleManager* mRestyleManager;
    AutoRestore<ReframingStyleContexts*> mRestorePointer;
    ReframingStyleContextTable mElementContexts;
    ReframingStyleContextTable mBeforePseudoContexts;
    ReframingStyleContextTable mAfterPseudoContexts;
  };

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

  /**
   * Try initiating a transition for an element or a ::before or ::after
   * pseudo-element, given an old and new style context.  This may
   * change the new style context if a transition is started.  Returns
   * true if it does change aNewStyleContext.
   *
   * For the pseudo-elements, aContent must be the anonymous content
   * that we're creating for that pseudo-element, not the real element.
   */
  static bool TryInitiatingTransition(
      nsPresContext* aPresContext, nsIContent* aContent,
      GeckoStyleContext* aOldStyleContext,
      RefPtr<GeckoStyleContext>* aNewStyleContext /* inout */);

 public:
  // Process any pending restyles. This should be called after
  // CreateNeededFrames.
  // Note: It's the caller's responsibility to make sure to wrap a
  // ProcessPendingRestyles 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.
  void ProcessPendingRestyles();

 private:
  // ProcessPendingRestyles calls into one of our RestyleTracker
  // objects.  It then calls back to these functions at the beginning
  // and end of its work.
  void BeginProcessingRestyles(RestyleTracker& aRestyleTracker);
  void EndProcessingRestyles();

 public:
  // Update styles for animations that are running on the compositor and
  // whose updating is suppressed on the main thread (to save
  // unnecessary work), while leaving all other aspects of style
  // out-of-date.
  //
  // Performs an animation-only style flush to make styles from
  // throttled transitions up-to-date prior to processing an unrelated
  // style change, so that any transitions triggered by that style
  // change produce correct results.
  //
  // In more detail:  when we're able to run animations on the
  // compositor, we sometimes "throttle" these animations by skipping
  // updating style data on the main thread.  However, whenever we
  // process a normal (non-animation) style change, any changes in
  // computed style on elements that have transition-* properties set
  // may need to trigger new transitions; this process requires knowing
  // both the old and new values of the property.  To do this correctly,
  // we need to have an up-to-date *old* value of the property on the
  // primary frame.  So the purpose of the mini-flush is to update the
  // style for all throttled transitions and animations to the current
  // animation state without making any other updates, so that when we
  // process the queued style updates we'll have correct old data to
  // compare against.  When we do this, we don't bother touching frames
  // other than primary frames.
  void UpdateOnlyAnimationStyles();

  // Rebuilds all style data by throwing out the old rule tree and
  // building a new one, and additionally applying aExtraHint (which
  // must not contain nsChangeHint_ReconstructFrame) to the root frame.
  //
  // aRestyleHint says which restyle hint to use for the computation;
  // the only sensible values to use are eRestyle_Subtree (which says
  // that the rebuild must run selector matching) and nsRestyleHint(0)
  // (which says that rerunning selector matching is not required.  (The
  // method adds eRestyle_ForceDescendants internally, and including it
  // in the restyle hint is harmless; some callers (e.g.,
  // nsPresContext::MediaFeatureValuesChanged) might do this for their
  // own reasons.)
  void RebuildAllStyleData(nsChangeHint aExtraHint, nsRestyleHint aRestyleHint);

  /**
   * Notify the frame constructor that an element needs to have its
   * style recomputed.
   * @param aElement: The element to be restyled.
   * @param aRestyleHint: Which nodes need to have selector matching run
   *                      on them.
   * @param aMinChangeHint: A minimum change hint for aContent and its
   *                        descendants.
   * @param aRestyleHintData: Additional data to go with aRestyleHint.
   */
  void PostRestyleEvent(Element* aElement, nsRestyleHint aRestyleHint,
                        nsChangeHint aMinChangeHint,
                        const RestyleHintData* aRestyleHintData = nullptr);

 public:
  /**
   * Asynchronously clear style data from the root frame downwards and ensure
   * it will all be rebuilt. This is safe to call anytime; it will schedule
   * a restyle and take effect next time style changes are flushed.
   * This method is used to recompute the style data when some change happens
   * outside of any style rules, like a color preference change or a change
   * in a system font size, or to fix things up when an optimization in the
   * style data has become invalid. We assume that the root frame will not
   * need to be reframed.
   *
   * For parameters, see RebuildAllStyleData.
   */
  void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
                                    nsRestyleHint aRestyleHint);

#ifdef DEBUG
  bool InRebuildAllStyleData() const { return mInRebuildAllStyleData; }
#endif

#ifdef RESTYLE_LOGGING
  /**
   * Returns whether a restyle event currently being processed by this
   * GeckoRestyleManager should be logged.
   */
  bool ShouldLogRestyle() { return ShouldLogRestyle(PresContext()); }

  /**
   * Returns whether a restyle event currently being processed for the
   * document with the specified nsPresContext should be logged.
   */
  static bool ShouldLogRestyle(nsPresContext* aPresContext) {
    return aPresContext->RestyleLoggingEnabled() &&
           (!aPresContext->TransitionManager()->InAnimationOnlyStyleUpdate() ||
            AnimationRestyleLoggingEnabled());
  }

  static bool RestyleLoggingInitiallyEnabled() {
    static bool enabled = getenv("MOZ_DEBUG_RESTYLE") != 0;
    return enabled;
  }

  static bool AnimationRestyleLoggingEnabled() {
    static bool animations = getenv("MOZ_DEBUG_RESTYLE_ANIMATIONS") != 0;
    return animations;
  }

  // Set MOZ_DEBUG_RESTYLE_STRUCTS to a comma-separated string of
  // style struct names -- such as "Font,SVGReset" -- to log the style context
  // tree and those cached struct pointers before each restyle.  This
  // function returns a bitfield of the structs named in the
  // environment variable.
  static uint32_t StructsToLog();

  static nsCString StructNamesToString(uint32_t aSIDs);
  int32_t& LoggingDepth() { return mLoggingDepth; }
#endif

  bool IsProcessingRestyles() { return mIsProcessingRestyles; }
  bool HasPendingRestyles() const;

 private:
  inline nsStyleSet* StyleSet() const {
    MOZ_ASSERT(PresContext()->StyleSet()->IsGecko(),
               "GeckoRestyleManager should only be used with a Gecko-flavored "
               "style backend");
    return PresContext()->StyleSet()->AsGecko();
  }

  /* aMinHint is the minimal change that should be made to the element */
  // XXXbz do we really need the aPrimaryFrame argument here?
  void RestyleElement(Element* aElement, nsIFrame* aPrimaryFrame,
                      nsChangeHint aMinHint, RestyleTracker& aRestyleTracker,
                      nsRestyleHint aRestyleHint,
                      const RestyleHintData& aRestyleHintData);

  void StartRebuildAllStyleData(RestyleTracker& aRestyleTracker);
  void FinishRebuildAllStyleData();

  bool ShouldStartRebuildAllFor(RestyleTracker& aRestyleTracker) {
    // When we process our primary restyle tracker and we have a pending
    // rebuild-all, we need to process it.
    return mDoRebuildAllStyleData && &aRestyleTracker == &mPendingRestyles;
  }

  void ProcessRestyles(RestyleTracker& aRestyleTracker) {
    // Fast-path the common case (esp. for the animation restyle
    // tracker) of not having anything to do.
    if (aRestyleTracker.Count() || ShouldStartRebuildAllFor(aRestyleTracker)) {
      IncrementRestyleGeneration();
      aRestyleTracker.DoProcessRestyles();
    }
  }

 private:
  // True if we need to reconstruct the rule tree the next time we
  // process restyles.
  bool mDoRebuildAllStyleData : 1;
  // True if we're currently in the process of reconstructing the rule tree.
  bool mInRebuildAllStyleData : 1;
  // Whether rule matching should skip styles associated with animation
  bool mSkipAnimationRules : 1;
  bool mHavePendingNonAnimationRestyles : 1;

  nsChangeHint mRebuildAllExtraHint;
  nsRestyleHint mRebuildAllRestyleHint;

  ReframingStyleContexts* mReframingStyleContexts;

  RestyleTracker mPendingRestyles;

  // Are we currently in the middle of a call to ProcessRestyles?
  // This flag is used both as a debugging aid to assert that we are not
  // performing nested calls to ProcessPendingRestyles, as well as to ignore
  // redundant calls to IncrementAnimationGeneration.
  bool mIsProcessingRestyles;

#ifdef RESTYLE_LOGGING
  int32_t mLoggingDepth;
#endif
};

/**
 * An ElementRestyler is created for *each* element in a subtree that we
 * recompute styles for.
 */
class ElementRestyler final {
 public:
  typedef mozilla::dom::Element Element;

  struct ContextToClear {
    RefPtr<GeckoStyleContext> mStyleContext;
    uint32_t mStructs;
  };

  // Construct for the root of the subtree that we're restyling.
  ElementRestyler(nsPresContext* aPresContext, nsIFrame* aFrame,
                  nsStyleChangeList* aChangeList,
                  nsChangeHint aHintsHandledByAncestors,
                  RestyleTracker& aRestyleTracker,
                  nsTArray<nsCSSSelector*>& aSelectorsForDescendants,
                  TreeMatchContext& aTreeMatchContext,
                  nsTArray<nsIContent*>& aVisibleKidsOfHiddenElement,
                  nsTArray<ContextToClear>& aContextsToClear,
                  nsTArray<RefPtr<GeckoStyleContext>>& aSwappedStructOwners);

  // Construct for an element whose parent is being restyled.
  enum ConstructorFlags { FOR_OUT_OF_FLOW_CHILD = 1 << 0 };
  ElementRestyler(const ElementRestyler& aParentRestyler, nsIFrame* aFrame,
                  uint32_t aConstructorFlags);

  // Construct for a frame whose parent is being restyled, but whose
  // style context is the parent style context for its parent frame.
  // (This is only used for table frames, whose style contexts are used
  // as the parent style context for their table wrapper frame. We should
  // probably try to get rid of this exception and have the inheritance go
  // the other way.)
  enum ParentContextFromChildFrame { PARENT_CONTEXT_FROM_CHILD_FRAME };
  ElementRestyler(ParentContextFromChildFrame,
                  const ElementRestyler& aParentFrameRestyler,
                  nsIFrame* aFrame);

  // For restyling undisplayed content only (mFrame==null).
  ElementRestyler(nsPresContext* aPresContext, nsIContent* aContent,
                  nsStyleChangeList* aChangeList,
                  nsChangeHint aHintsHandledByAncestors,
                  RestyleTracker& aRestyleTracker,
                  nsTArray<nsCSSSelector*>& aSelectorsForDescendants,
                  TreeMatchContext& aTreeMatchContext,
                  nsTArray<nsIContent*>& aVisibleKidsOfHiddenElement,
                  nsTArray<ContextToClear>& aContextsToClear,
                  nsTArray<RefPtr<GeckoStyleContext>>& aSwappedStructOwners);

  /**
   * Restyle our frame's element and its subtree.
   *
   * Use eRestyle_Self for the aRestyleHint argument to mean
   * "reresolve our style context but not kids", use eRestyle_Subtree
   * to mean "reresolve our style context and kids", and use
   * nsRestyleHint(0) to mean recompute a new style context for our
   * current parent and existing rulenode, and the same for kids.
   */
  void Restyle(nsRestyleHint aRestyleHint);

  /**
   * mHintsHandledBySelf changes over time; it starts off as nsChangeHint(0),
   * and by the end of Restyle it represents the hints that have been handled
   * for this frame.  This method is intended to be called after Restyle, to
   * find out what hints have been handled for this frame.
   */
  nsChangeHint HintsHandledForFrame() { return mHintsHandledBySelf; }

  /**
   * Called from GeckoRestyleManager::ComputeAndProcessStyleChange to restyle
   * children of a display:contents element.
   */
  void RestyleChildrenOfDisplayContentsElement(
      nsIFrame* aParentFrame, GeckoStyleContext* aNewContext,
      nsChangeHint aMinHint, RestyleTracker& aRestyleTracker,
      nsRestyleHint aRestyleHint, const RestyleHintData& aRestyleHintData);

  /**
   * Re-resolve the style contexts for a frame tree, building aChangeList
   * based on the resulting style changes, plus aMinChange applied to aFrame.
   */
  static void ComputeStyleChangeFor(
      nsIFrame* aFrame, nsStyleChangeList* aChangeList, nsChangeHint aMinChange,
      RestyleTracker& aRestyleTracker, nsRestyleHint aRestyleHint,
      const RestyleHintData& aRestyleHintData,
      nsTArray<ContextToClear>& aContextsToClear,
      nsTArray<RefPtr<GeckoStyleContext>>& aSwappedStructOwners);

#ifdef RESTYLE_LOGGING
  bool ShouldLogRestyle() {
    return GeckoRestyleManager::ShouldLogRestyle(mPresContext);
  }
#endif

 private:
  inline nsStyleSet* StyleSet() const;

  // Enum class for the result of RestyleSelf, which indicates whether the
  // restyle procedure should continue to the children, and how.
  //
  // These values must be ordered so that later values imply that all
  // the work of the earlier values is also done.
  enum class RestyleResult : uint8_t {
    // default initial value
    eNone,

    // we left the old style context on the frame; do not restyle children
    eStop,

    // we got a new style context on this frame, but we know that children
    // do not depend on the changed values; do not restyle children
    eStopWithStyleChange,

    // continue restyling children
    eContinue,

    // continue restyling children with eRestyle_ForceDescendants set
    eContinueAndForceDescendants
  };

  struct SwapInstruction {
    RefPtr<GeckoStyleContext> mOldContext;
    RefPtr<GeckoStyleContext> mNewContext;
    uint32_t mStructsToSwap;
  };

  /**
   * First half of Restyle().
   */
  RestyleResult RestyleSelf(nsIFrame* aSelf, nsRestyleHint aRestyleHint,
                            uint32_t* aSwappedStructs,
                            nsTArray<SwapInstruction>& aSwaps);

  /**
   * Restyle the children of this frame (and, in turn, their children).
   *
   * Second half of Restyle().
   */
  void RestyleChildren(nsRestyleHint aChildRestyleHint);

  /**
   * Returns true iff a selector in mSelectorsForDescendants matches aElement.
   * This is called when processing a eRestyle_SomeDescendants restyle hint.
   */
  bool SelectorMatchesForRestyle(Element* aElement);

  /**
   * Returns true iff aRestyleHint indicates that we should be restyling.
   * Specifically, this will return true when eRestyle_Self or
   * eRestyle_Subtree is present, or if eRestyle_SomeDescendants is
   * present and the specified element matches one of the selectors in
   * mSelectorsForDescendants.
   */
  bool MustRestyleSelf(nsRestyleHint aRestyleHint, Element* aElement);

  /**
   * Returns true iff aRestyleHint indicates that we can call
   * ReparentStyleContext rather than any other restyling method of
   * nsStyleSet that looks up a new rule node, and if we are
   * not in the process of reconstructing the whole rule tree.
   * This is used to check whether it is appropriate to call
   * ReparentStyleContext.
   */
  bool CanReparentStyleContext(nsRestyleHint aRestyleHint);

  /**
   * Helpers for Restyle().
   */
  bool MoveStyleContextsForContentChildren(
      nsIFrame* aParent, GeckoStyleContext* aOldContext,
      nsTArray<GeckoStyleContext*>& aContextsToMove);
  bool MoveStyleContextsForChildren(GeckoStyleContext* aOldContext);

  /**
   * Helpers for RestyleSelf().
   */
  void CaptureChange(GeckoStyleContext* aOldContext,
                     GeckoStyleContext* aNewContext,
                     nsChangeHint aChangeToAssume, uint32_t* aEqualStructs,
                     uint32_t* aSamePointerStructs);
  void ComputeRestyleResultFromFrame(nsIFrame* aSelf,
                                     RestyleResult& aRestyleResult,
                                     bool& aCanStopWithStyleChange);
  void ComputeRestyleResultFromNewContext(nsIFrame* aSelf,
                                          GeckoStyleContext* aNewContext,
                                          RestyleResult& aRestyleResult,
                                          bool& aCanStopWithStyleChange);

  // Helpers for RestyleChildren().
  void RestyleUndisplayedDescendants(nsRestyleHint aChildRestyleHint);
  bool MustCheckUndisplayedContent(nsIFrame* aFrame,
                                   nsIContent*& aUndisplayedParent);

  /**
   * In the following two methods, aParentStyleContext is either
   * mFrame->StyleContext() if we have a frame, or a display:contents
   * style context if we don't.
   */
  void DoRestyleUndisplayedDescendants(nsRestyleHint aChildRestyleHint,
                                       nsIContent* aParent,
                                       GeckoStyleContext* aParentStyleContext);
  void RestyleUndisplayedNodes(nsRestyleHint aChildRestyleHint,
                               UndisplayedNode* aUndisplayed,
                               nsIContent* aUndisplayedParent,
                               GeckoStyleContext* aParentStyleContext,
                               const StyleDisplay aDisplay);
  void MaybeReframeForBeforePseudo();
  void MaybeReframeForAfterPseudo(nsIFrame* aFrame);
  void MaybeReframeForPseudo(CSSPseudoElementType aPseudoType,
                             nsIFrame* aGenConParentFrame, nsIFrame* aFrame,
                             nsIContent* aContent,
                             GeckoStyleContext* aStyleContext);
#ifdef DEBUG
  bool MustReframeForBeforePseudo();
  bool MustReframeForAfterPseudo(nsIFrame* aFrame);
#endif
  bool MustReframeForPseudo(CSSPseudoElementType aPseudoType,
                            nsIFrame* aGenConParentFrame, nsIFrame* aFrame,
                            nsIContent* aContent,
                            GeckoStyleContext* aStyleContext);
  void RestyleContentChildren(nsIFrame* aParent,
                              nsRestyleHint aChildRestyleHint);
  void InitializeAccessibilityNotifications(nsStyleContext* aNewContext);
  void SendAccessibilityNotifications();

  enum DesiredA11yNotifications {
    eSkipNotifications,
    eSendAllNotifications,
    eNotifyIfShown
  };

  enum A11yNotificationType { eDontNotify, eNotifyShown, eNotifyHidden };

  // These methods handle the eRestyle_SomeDescendants hint by traversing
  // down the frame tree (and then when reaching undisplayed content,
  // the flattened content tree) find elements that match a selector
  // in mSelectorsForDescendants and call AddPendingRestyle for them.
  void ConditionallyRestyleChildren();
  void ConditionallyRestyleChildren(nsIFrame* aFrame, Element* aRestyleRoot);
  void ConditionallyRestyleContentChildren(nsIFrame* aFrame,
                                           Element* aRestyleRoot);
  void ConditionallyRestyleUndisplayedDescendants(nsIFrame* aFrame,
                                                  Element* aRestyleRoot);
  void DoConditionallyRestyleUndisplayedDescendants(nsIContent* aParent,
                                                    Element* aRestyleRoot);
  void ConditionallyRestyleUndisplayedNodes(UndisplayedNode* aUndisplayed,
                                            nsIContent* aUndisplayedParent,
                                            const StyleDisplay aDisplay,
                                            Element* aRestyleRoot);
  void ConditionallyRestyleContentDescendants(Element* aElement,
                                              Element* aRestyleRoot);
  bool ConditionallyRestyle(nsIFrame* aFrame, Element* aRestyleRoot);
  bool ConditionallyRestyle(Element* aElement, Element* aRestyleRoot);

#ifdef RESTYLE_LOGGING
  int32_t& LoggingDepth() { return mLoggingDepth; }
#endif

#ifdef DEBUG
  static nsCString RestyleResultToString(RestyleResult aRestyleResult);
#endif

 private:
  nsPresContext* const mPresContext;
  nsIFrame* const mFrame;
  nsIContent* const mParentContent;
  // |mContent| is the node that we used for rule matching of
  // normal elements (not pseudo-elements) and for which we generate
  // framechange hints if we need them.
  nsIContent* const mContent;
  nsStyleChangeList* const mChangeList;
  // Hints that we computed on an ancestor (and which we already have
  // generated a change list entry for).  When we traverse to children
  // after restyling an element, this field accumulates the hints
  // generated for that element.
  const nsChangeHint mHintsHandledByAncestors;
  // Hints that we have computed so far the current node.  This is
  // initially zero, and accumulates hints for each same-style continuation
  // and {ib} split sibling we restyle for the node.
  nsChangeHint mHintsHandledBySelf;
  RestyleTracker& mRestyleTracker;
  nsTArray<nsCSSSelector*>& mSelectorsForDescendants;
  TreeMatchContext& mTreeMatchContext;
  nsIFrame* mResolvedChild;  // child that provides our parent style context
  // Array of style context subtrees in which we need to clear out cached
  // structs at the end of the restyle (after change hints have been
  // processed).
  nsTArray<ContextToClear>& mContextsToClear;
  // Style contexts that had old structs swapped into it and which should
  // stay alive until the end of the restyle.  (See comment in
  // ElementRestyler::Restyle.)
  nsTArray<RefPtr<GeckoStyleContext>>& mSwappedStructOwners;
  // Whether this is the root of the restyle.
  bool mIsRootOfRestyle;

#ifdef ACCESSIBILITY
  const DesiredA11yNotifications mDesiredA11yNotifications;
  DesiredA11yNotifications mKidsDesiredA11yNotifications;
  A11yNotificationType mOurA11yNotification;
  nsTArray<nsIContent*>& mVisibleKidsOfHiddenElement;
  bool mWasFrameVisible;
#endif

#ifdef RESTYLE_LOGGING
  int32_t mLoggingDepth;
#endif
};

/**
 * This pushes any display:contents nodes onto a TreeMatchContext.
 * Use it before resolving style for kids of aParent where aParent
 * (and further ancestors) may be display:contents nodes which have
 * not yet been pushed onto TreeMatchContext.
 */
class MOZ_RAII AutoDisplayContentsAncestorPusher final {
 public:
  typedef mozilla::dom::Element Element;
  AutoDisplayContentsAncestorPusher(TreeMatchContext& aTreeMatchContext,
                                    nsPresContext* aPresContext,
                                    nsIContent* aParent);
  ~AutoDisplayContentsAncestorPusher();
  bool IsEmpty() const { return mAncestors.Length() == 0; }

 private:
  TreeMatchContext& mTreeMatchContext;
  nsPresContext* const mPresContext;
  AutoTArray<mozilla::dom::Element*, 4> mAncestors;
};

}  // namespace mozilla

#endif /* mozilla_GeckoRestyleManager_h */