summaryrefslogtreecommitdiffstats
path: root/layout/base/RestyleManager.h
diff options
context:
space:
mode:
Diffstat (limited to 'layout/base/RestyleManager.h')
-rw-r--r--layout/base/RestyleManager.h885
1 files changed, 885 insertions, 0 deletions
diff --git a/layout/base/RestyleManager.h b/layout/base/RestyleManager.h
new file mode 100644
index 000000000..e22fe9058
--- /dev/null
+++ b/layout/base/RestyleManager.h
@@ -0,0 +1,885 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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_RestyleManager_h
+#define mozilla_RestyleManager_h
+
+#include "mozilla/RestyleLogging.h"
+#include "mozilla/RestyleManagerBase.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 RestyleManager final : public RestyleManagerBase
+{
+public:
+ typedef RestyleManagerBase base_type;
+
+ friend class RestyleTracker;
+ friend class ElementRestyler;
+
+ explicit RestyleManager(nsPresContext* aPresContext);
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~RestyleManager()
+ {
+ MOZ_ASSERT(!mReframingStyleContexts,
+ "temporary member should be nulled out before destruction");
+ MOZ_ASSERT(!mAnimationsWithDestroyedFrame,
+ "leaving dangling pointers from AnimationsWithDestroyedFrame");
+ }
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(mozilla::RestyleManager)
+
+ // Forwarded nsIDocumentObserver method, to handle restyling (and
+ // passing the notification to the frame).
+ nsresult ContentStateChanged(nsIContent* aContent,
+ EventStates aStateMask);
+
+ // Forwarded nsIMutationObserver method, to handle restyling.
+ void AttributeWillChange(Element* aElement,
+ int32_t aNameSpaceID,
+ nsIAtom* 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,
+ nsIAtom* aAttribute,
+ int32_t aModType,
+ const nsAttrValue* aOldValue);
+
+ // 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; }
+
+ static uint64_t GetAnimationGenerationForFrame(nsIFrame* aFrame);
+
+ // 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() {
+ // We update the animation generation at start of each call to
+ // ProcessPendingRestyles so we should ignore any subsequent (redundant)
+ // calls that occur while we are still processing restyles.
+ if (!mIsProcessingRestyles) {
+ ++mAnimationGeneration;
+ }
+ }
+
+ // 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);
+
+ void ClearSelectors() {
+ mPendingRestyles.ClearSelectors();
+ }
+
+private:
+ // 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(nsStyleContext* 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>, nsStyleContext>
+ 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(RestyleManager* aRestyleManager);
+ ~ReframingStyleContexts();
+
+ void Put(nsIContent* aContent, nsStyleContext* 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);
+ }
+ }
+
+ nsStyleContext* 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:
+ RestyleManager* 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,
+ nsStyleContext* aOldStyleContext,
+ RefPtr<nsStyleContext>* aNewStyleContext /* inout */);
+
+ // 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 {
+ public:
+ // 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.)
+ explicit AnimationsWithDestroyedFrame(RestyleManager* aRestyleManager);
+
+ // 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.
+ void Put(nsIContent* aContent, nsStyleContext* aStyleContext) {
+ MOZ_ASSERT(aContent);
+ CSSPseudoElementType pseudoType = aStyleContext->GetPseudoType();
+ if (pseudoType == CSSPseudoElementType::NotPseudo) {
+ mContents.AppendElement(aContent);
+ } else if (pseudoType == CSSPseudoElementType::before) {
+ MOZ_ASSERT(aContent->NodeInfo()->NameAtom() == nsGkAtoms::mozgeneratedcontentbefore);
+ mBeforeContents.AppendElement(aContent->GetParent());
+ } else if (pseudoType == CSSPseudoElementType::after) {
+ MOZ_ASSERT(aContent->NodeInfo()->NameAtom() == nsGkAtoms::mozgeneratedcontentafter);
+ mAfterContents.AppendElement(aContent->GetParent());
+ }
+ }
+
+ void StopAnimationsForElementsWithoutFrames();
+
+ private:
+ void StopAnimationsWithoutFrame(nsTArray<RefPtr<nsIContent>>& aArray,
+ CSSPseudoElementType aPseudoType);
+
+ RestyleManager* mRestyleManager;
+ AutoRestore<AnimationsWithDestroyedFrame*> mRestorePointer;
+
+ // Below three arrays might include elements that have already had their
+ // animations or transitions stopped.
+ //
+ // mBeforeContents and mAfterContents hold the real element rather than
+ // the content node for the generated content (which might change during
+ // a reframe)
+ nsTArray<RefPtr<nsIContent>> mContents;
+ nsTArray<RefPtr<nsIContent>> mBeforeContents;
+ nsTArray<RefPtr<nsIContent>> mAfterContents;
+ };
+
+ /**
+ * Return the current AnimationsWithDestroyedFrame struct, or null if we're
+ * not currently in a restyling operation.
+ */
+ AnimationsWithDestroyedFrame* GetAnimationsWithDestroyedFrame() {
+ return mAnimationsWithDestroyedFrame;
+ }
+
+private:
+ void RestyleForEmptyChange(Element* aContainer);
+
+public:
+ // Handle ContentInserted notifications.
+ void ContentInserted(nsINode* aContainer, nsIContent* aChild)
+ {
+ RestyleForInsertOrChange(aContainer, aChild);
+ }
+
+ // Handle ContentAppended notifications.
+ void ContentAppended(nsIContent* aContainer, nsIContent* aFirstNewContent)
+ {
+ RestyleForAppend(aContainer, aFirstNewContent);
+ }
+
+ // Handle ContentRemoved notifications.
+ //
+ // 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
+ // following sibling in addition to the old child. |aContainer| must be
+ // non-null; when the container is null, no work is needed. aFollowingSibling
+ // is the sibling that used to come after aOldChild before the removal.
+ void ContentRemoved(nsINode* aContainer, nsIContent* aOldChild,
+ nsIContent* aFollowingSibling);
+
+ // Restyling for a ContentInserted (notification after insertion) or
+ // for a CharacterDataChanged. |aContainer| must be non-null; when
+ // the container is null, no work is needed.
+ void RestyleForInsertOrChange(nsINode* aContainer, nsIContent* aChild);
+
+ // Restyling for a ContentAppended (notification after insertion) or
+ // for a CharacterDataChanged. |aContainer| must be non-null; when
+ // the container is null, no work is needed.
+ void RestyleForAppend(nsIContent* aContainer, nsIContent* aFirstNewContent);
+
+ // 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();
+
+ // Returns whether there are any pending restyles.
+ bool HasPendingRestyles() { return mPendingRestyles.Count() != 0; }
+
+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);
+
+ void PostRestyleEventForLazyConstruction()
+ {
+ PostRestyleEventInternal(true);
+ }
+
+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
+ * RestyleManager 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
+
+private:
+ inline nsStyleSet* StyleSet() const {
+ MOZ_ASSERT(PresContext()->StyleSet()->IsGecko(),
+ "RestyleManager 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;
+
+ // The total number of animation flushes by this frame constructor.
+ // Used to keep the layer and animation manager in sync.
+ uint64_t mAnimationGeneration;
+
+ ReframingStyleContexts* mReframingStyleContexts;
+ AnimationsWithDestroyedFrame* mAnimationsWithDestroyedFrame;
+
+ 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<nsStyleContext> 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<nsStyleContext>>& 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<nsStyleContext>>& 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);
+
+ /**
+ * mHintsHandled changes over time; it starts off as the hints that
+ * have been handled by ancestors, 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 mHintsHandled; }
+
+ /**
+ * Called from RestyleManager::ComputeAndProcessStyleChange to restyle
+ * children of a display:contents element.
+ */
+ void RestyleChildrenOfDisplayContentsElement(nsIFrame* aParentFrame,
+ nsStyleContext* 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<nsStyleContext>>&
+ aSwappedStructOwners);
+
+#ifdef RESTYLE_LOGGING
+ bool ShouldLogRestyle() {
+ return RestyleManager::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<nsStyleContext> mOldContext;
+ RefPtr<nsStyleContext> 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().
+ */
+ void AddLayerChangesForAnimation();
+
+ bool MoveStyleContextsForContentChildren(nsIFrame* aParent,
+ nsStyleContext* aOldContext,
+ nsTArray<nsStyleContext*>& aContextsToMove);
+ bool MoveStyleContextsForChildren(nsStyleContext* aOldContext);
+
+ /**
+ * Helpers for RestyleSelf().
+ */
+ void CaptureChange(nsStyleContext* aOldContext,
+ nsStyleContext* aNewContext,
+ nsChangeHint aChangeToAssume,
+ uint32_t* aEqualStructs,
+ uint32_t* aSamePointerStructs);
+ void ComputeRestyleResultFromFrame(nsIFrame* aSelf,
+ RestyleResult& aRestyleResult,
+ bool& aCanStopWithStyleChange);
+ void ComputeRestyleResultFromNewContext(nsIFrame* aSelf,
+ nsStyleContext* 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,
+ nsStyleContext* aParentStyleContext);
+ void RestyleUndisplayedNodes(nsRestyleHint aChildRestyleHint,
+ UndisplayedNode* aUndisplayed,
+ nsIContent* aUndisplayedParent,
+ nsStyleContext* aParentStyleContext,
+ const StyleDisplay aDisplay);
+ void MaybeReframeForBeforePseudo();
+ void MaybeReframeForAfterPseudo(nsIFrame* aFrame);
+ void MaybeReframeForPseudo(CSSPseudoElementType aPseudoType,
+ nsIFrame* aGenConParentFrame,
+ nsIFrame* aFrame,
+ nsIContent* aContent,
+ nsStyleContext* aStyleContext);
+#ifdef DEBUG
+ bool MustReframeForBeforePseudo();
+ bool MustReframeForAfterPseudo(nsIFrame* aFrame);
+#endif
+ bool MustReframeForPseudo(CSSPseudoElementType aPseudoType,
+ nsIFrame* aGenConParentFrame,
+ nsIFrame* aFrame,
+ nsIContent* aContent,
+ nsStyleContext* 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;
+ // We have already generated change list entries for hints listed in
+ // mHintsHandled (initially it's those handled by ancestors, but by
+ // the end of Restyle it is those handled for this frame as well). We
+ // need to generate a new change list entry for the frame when its
+ // style comparision returns a hint other than one of these hints.
+ nsChangeHint mHintsHandled;
+ // See nsStyleContext::CalcStyleDifference
+ nsChangeHint mParentFrameHintsNotHandledForDescendants;
+ nsChangeHint mHintsNotHandledForDescendants;
+ 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<nsStyleContext>>& 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_RestyleManager_h */