diff options
Diffstat (limited to 'layout/base/RestyleManager.h')
-rw-r--r-- | layout/base/RestyleManager.h | 885 |
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 */ |