michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /** michael@0: * Code responsible for managing style changes: tracking what style michael@0: * changes need to happen, scheduling them, and doing them. michael@0: */ michael@0: michael@0: #ifndef mozilla_RestyleManager_h michael@0: #define mozilla_RestyleManager_h michael@0: michael@0: #include "nsISupportsImpl.h" michael@0: #include "nsChangeHint.h" michael@0: #include "RestyleTracker.h" michael@0: #include "nsPresContext.h" michael@0: michael@0: class nsRefreshDriver; michael@0: class nsIFrame; michael@0: struct TreeMatchContext; michael@0: michael@0: namespace mozilla { michael@0: class EventStates; michael@0: michael@0: namespace dom { michael@0: class Element; michael@0: } // namespace dom michael@0: michael@0: class RestyleManager MOZ_FINAL { michael@0: public: michael@0: friend class ::nsRefreshDriver; michael@0: friend class RestyleTracker; michael@0: michael@0: typedef mozilla::dom::Element Element; michael@0: michael@0: RestyleManager(nsPresContext* aPresContext); michael@0: michael@0: private: michael@0: // Private destructor, to discourage deletion outside of Release(): michael@0: ~RestyleManager() michael@0: { michael@0: } michael@0: michael@0: public: michael@0: NS_INLINE_DECL_REFCOUNTING(mozilla::RestyleManager) michael@0: michael@0: void Disconnect() { michael@0: mPresContext = nullptr; michael@0: } michael@0: michael@0: nsPresContext* PresContext() const { michael@0: MOZ_ASSERT(mPresContext); michael@0: return mPresContext; michael@0: } michael@0: michael@0: nsCSSFrameConstructor* FrameConstructor() const michael@0: { return PresContext()->FrameConstructor(); } michael@0: michael@0: // Should be called when a frame is going to be destroyed and michael@0: // WillDestroyFrameTree hasn't been called yet. michael@0: void NotifyDestroyingFrame(nsIFrame* aFrame); michael@0: michael@0: // Forwarded nsIDocumentObserver method, to handle restyling (and michael@0: // passing the notification to the frame). michael@0: nsresult ContentStateChanged(nsIContent* aContent, michael@0: EventStates aStateMask); michael@0: michael@0: // Forwarded nsIMutationObserver method, to handle restyling. michael@0: void AttributeWillChange(Element* aElement, michael@0: int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType); michael@0: // Forwarded nsIMutationObserver method, to handle restyling (and michael@0: // passing the notification to the frame). michael@0: void AttributeChanged(Element* aElement, michael@0: int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType); michael@0: michael@0: // Get an integer that increments every time there is a style change michael@0: // as a result of a change to the :hover content state. michael@0: uint32_t GetHoverGeneration() const { return mHoverGeneration; } michael@0: michael@0: // Get a counter that increments on every style change, that we use to michael@0: // track whether off-main-thread animations are up-to-date. michael@0: uint64_t GetAnimationGeneration() const { return mAnimationGeneration; } michael@0: michael@0: /** michael@0: * Reparent the style contexts of this frame subtree. The parent frame of michael@0: * aFrame must be changed to the new parent before this function is called; michael@0: * the new parent style context will be automatically computed based on the michael@0: * new position in the frame tree. michael@0: * michael@0: * @param aFrame the root of the subtree to reparent. Must not be null. michael@0: */ michael@0: NS_HIDDEN_(nsresult) ReparentStyleContext(nsIFrame* aFrame); michael@0: michael@0: /** michael@0: * Re-resolve the style contexts for a frame tree, building michael@0: * aChangeList based on the resulting style changes, plus aMinChange michael@0: * applied to aFrame. michael@0: */ michael@0: NS_HIDDEN_(void) michael@0: ComputeStyleChangeFor(nsIFrame* aFrame, michael@0: nsStyleChangeList* aChangeList, michael@0: nsChangeHint aMinChange, michael@0: RestyleTracker& aRestyleTracker, michael@0: bool aRestyleDescendants); michael@0: michael@0: #ifdef DEBUG michael@0: /** michael@0: * DEBUG ONLY method to verify integrity of style tree versus frame tree michael@0: */ michael@0: NS_HIDDEN_(void) DebugVerifyStyleTree(nsIFrame* aFrame); michael@0: #endif michael@0: michael@0: // Note: It's the caller's responsibility to make sure to wrap a michael@0: // ProcessRestyledFrames call in a view update batch and a script blocker. michael@0: // This function does not call ProcessAttachedQueue() on the binding manager. michael@0: // If the caller wants that to happen synchronously, it needs to handle that michael@0: // itself. michael@0: nsresult ProcessRestyledFrames(nsStyleChangeList& aRestyleArray); michael@0: michael@0: private: michael@0: void RestyleForEmptyChange(Element* aContainer); michael@0: michael@0: public: michael@0: // Restyling for a ContentInserted (notification after insertion) or michael@0: // for a CharacterDataChanged. |aContainer| must be non-null; when michael@0: // the container is null, no work is needed. michael@0: void RestyleForInsertOrChange(Element* aContainer, nsIContent* aChild); michael@0: michael@0: // This would be the same as RestyleForInsertOrChange if we got the michael@0: // notification before the removal. However, we get it after, so we need the michael@0: // following sibling in addition to the old child. |aContainer| must be michael@0: // non-null; when the container is null, no work is needed. aFollowingSibling michael@0: // is the sibling that used to come after aOldChild before the removal. michael@0: void RestyleForRemove(Element* aContainer, michael@0: nsIContent* aOldChild, michael@0: nsIContent* aFollowingSibling); michael@0: michael@0: // Same for a ContentAppended. |aContainer| must be non-null; when michael@0: // the container is null, no work is needed. michael@0: void RestyleForAppend(Element* aContainer, nsIContent* aFirstNewContent); michael@0: michael@0: // Process any pending restyles. This should be called after michael@0: // CreateNeededFrames. michael@0: // Note: It's the caller's responsibility to make sure to wrap a michael@0: // ProcessPendingRestyles call in a view update batch and a script blocker. michael@0: // This function does not call ProcessAttachedQueue() on the binding manager. michael@0: // If the caller wants that to happen synchronously, it needs to handle that michael@0: // itself. michael@0: void ProcessPendingRestyles(); michael@0: michael@0: // ProcessPendingRestyles calls into one of our RestyleTracker michael@0: // objects. It then calls back to these functions at the beginning michael@0: // and end of its work. michael@0: void BeginProcessingRestyles(); michael@0: void EndProcessingRestyles(); michael@0: michael@0: // Rebuilds all style data by throwing out the old rule tree and michael@0: // building a new one, and additionally applying aExtraHint (which michael@0: // must not contain nsChangeHint_ReconstructFrame) to the root frame. michael@0: void RebuildAllStyleData(nsChangeHint aExtraHint); michael@0: michael@0: // Helper that does part of the work of RebuildAllStyleData, shared by michael@0: // RestyleElement for 'rem' handling. michael@0: void DoRebuildAllStyleData(RestyleTracker& aRestyleTracker, michael@0: nsChangeHint aExtraHint); michael@0: michael@0: // See PostRestyleEventCommon below. michael@0: void PostRestyleEvent(Element* aElement, michael@0: nsRestyleHint aRestyleHint, michael@0: nsChangeHint aMinChangeHint) michael@0: { michael@0: if (mPresContext) { michael@0: PostRestyleEventCommon(aElement, aRestyleHint, aMinChangeHint, michael@0: mPresContext->IsProcessingAnimationStyleChange()); michael@0: } michael@0: } michael@0: michael@0: // See PostRestyleEventCommon below. michael@0: void PostAnimationRestyleEvent(Element* aElement, michael@0: nsRestyleHint aRestyleHint, michael@0: nsChangeHint aMinChangeHint) michael@0: { michael@0: PostRestyleEventCommon(aElement, aRestyleHint, aMinChangeHint, true); michael@0: } michael@0: michael@0: void PostRestyleEventForLazyConstruction() michael@0: { michael@0: PostRestyleEventInternal(true); michael@0: } michael@0: michael@0: void FlushOverflowChangedTracker() michael@0: { michael@0: mOverflowChangedTracker.Flush(); michael@0: } michael@0: michael@0: private: michael@0: /** michael@0: * Notify the frame constructor that an element needs to have its michael@0: * style recomputed. michael@0: * @param aElement: The element to be restyled. michael@0: * @param aRestyleHint: Which nodes need to have selector matching run michael@0: * on them. michael@0: * @param aMinChangeHint: A minimum change hint for aContent and its michael@0: * descendants. michael@0: * @param aForAnimation: Whether the style should be computed with or michael@0: * without animation data. Animation code michael@0: * sometimes needs to pass true; other code michael@0: * should generally pass the the pres context's michael@0: * IsProcessingAnimationStyleChange() value michael@0: * (which is the default value). michael@0: */ michael@0: void PostRestyleEventCommon(Element* aElement, michael@0: nsRestyleHint aRestyleHint, michael@0: nsChangeHint aMinChangeHint, michael@0: bool aForAnimation); michael@0: void PostRestyleEventInternal(bool aForLazyConstruction); michael@0: michael@0: public: michael@0: /** michael@0: * Asynchronously clear style data from the root frame downwards and ensure michael@0: * it will all be rebuilt. This is safe to call anytime; it will schedule michael@0: * a restyle and take effect next time style changes are flushed. michael@0: * This method is used to recompute the style data when some change happens michael@0: * outside of any style rules, like a color preference change or a change michael@0: * in a system font size, or to fix things up when an optimization in the michael@0: * style data has become invalid. We assume that the root frame will not michael@0: * need to be reframed. michael@0: */ michael@0: void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint); michael@0: michael@0: private: michael@0: /* aMinHint is the minimal change that should be made to the element */ michael@0: // XXXbz do we really need the aPrimaryFrame argument here? michael@0: void RestyleElement(Element* aElement, michael@0: nsIFrame* aPrimaryFrame, michael@0: nsChangeHint aMinHint, michael@0: RestyleTracker& aRestyleTracker, michael@0: bool aRestyleDescendants); michael@0: michael@0: nsresult StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint); michael@0: michael@0: // Returns true if this function managed to successfully move a frame, and michael@0: // false if it could not process the position change, and a reflow should michael@0: // be performed instead. michael@0: bool RecomputePosition(nsIFrame* aFrame); michael@0: michael@0: private: michael@0: nsPresContext* mPresContext; // weak, disconnected in Disconnect michael@0: michael@0: bool mRebuildAllStyleData : 1; michael@0: // True if we're already waiting for a refresh notification michael@0: bool mObservingRefreshDriver : 1; michael@0: // True if we're in the middle of a nsRefreshDriver refresh michael@0: bool mInStyleRefresh : 1; michael@0: uint32_t mHoverGeneration; michael@0: nsChangeHint mRebuildAllExtraHint; michael@0: michael@0: OverflowChangedTracker mOverflowChangedTracker; michael@0: michael@0: // The total number of animation flushes by this frame constructor. michael@0: // Used to keep the layer and animation manager in sync. michael@0: uint64_t mAnimationGeneration; michael@0: michael@0: RestyleTracker mPendingRestyles; michael@0: RestyleTracker mPendingAnimationRestyles; michael@0: }; michael@0: michael@0: /** michael@0: * An ElementRestyler is created for *each* element in a subtree that we michael@0: * recompute styles for. michael@0: */ michael@0: class ElementRestyler MOZ_FINAL { michael@0: public: michael@0: typedef mozilla::dom::Element Element; michael@0: michael@0: // Construct for the root of the subtree that we're restyling. michael@0: ElementRestyler(nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: nsStyleChangeList* aChangeList, michael@0: nsChangeHint aHintsHandledByAncestors, michael@0: RestyleTracker& aRestyleTracker, michael@0: TreeMatchContext& aTreeMatchContext, michael@0: nsTArray& aVisibleKidsOfHiddenElement); michael@0: michael@0: // Construct for an element whose parent is being restyled. michael@0: enum ConstructorFlags { michael@0: FOR_OUT_OF_FLOW_CHILD = 1<<0 michael@0: }; michael@0: ElementRestyler(const ElementRestyler& aParentRestyler, michael@0: nsIFrame* aFrame, michael@0: uint32_t aConstructorFlags); michael@0: michael@0: // Construct for a frame whose parent is being restyled, but whose michael@0: // style context is the parent style context for its parent frame. michael@0: // (This is only used for table frames, whose style contexts are used michael@0: // as the parent style context for their outer table frame (table michael@0: // wrapper frame). We should probably try to get rid of this michael@0: // exception and have the inheritance go the other way.) michael@0: enum ParentContextFromChildFrame { PARENT_CONTEXT_FROM_CHILD_FRAME }; michael@0: ElementRestyler(ParentContextFromChildFrame, michael@0: const ElementRestyler& aParentFrameRestyler, michael@0: nsIFrame* aFrame); michael@0: michael@0: /** michael@0: * Restyle our frame's element and its subtree. michael@0: * michael@0: * Use eRestyle_Self for the aRestyleHint argument to mean michael@0: * "reresolve our style context but not kids", use eRestyle_Subtree michael@0: * to mean "reresolve our style context and kids", and use michael@0: * nsRestyleHint(0) to mean recompute a new style context for our michael@0: * current parent and existing rulenode, and the same for kids. michael@0: */ michael@0: void Restyle(nsRestyleHint aRestyleHint); michael@0: michael@0: /** michael@0: * mHintsHandled changes over time; it starts off as the hints that michael@0: * have been handled by ancestors, and by the end of Restyle it michael@0: * represents the hints that have been handled for this frame. This michael@0: * method is intended to be called after Restyle, to find out what michael@0: * hints have been handled for this frame. michael@0: */ michael@0: nsChangeHint HintsHandledForFrame() { return mHintsHandled; } michael@0: michael@0: private: michael@0: /** michael@0: * First half of Restyle(). michael@0: */ michael@0: void RestyleSelf(nsIFrame* aSelf, nsRestyleHint aRestyleHint); michael@0: michael@0: /** michael@0: * Restyle the children of this frame (and, in turn, their children). michael@0: * michael@0: * Second half of Restyle(). michael@0: */ michael@0: void RestyleChildren(nsRestyleHint aChildRestyleHint); michael@0: michael@0: /** michael@0: * Helper for RestyleSelf(). michael@0: */ michael@0: void CaptureChange(nsStyleContext* aOldContext, michael@0: nsStyleContext* aNewContext, michael@0: nsChangeHint aChangeToAssume); michael@0: michael@0: /** michael@0: * Helpers for RestyleChildren(). michael@0: */ michael@0: void RestyleUndisplayedChildren(nsRestyleHint aChildRestyleHint); michael@0: void RestyleBeforePseudo(); michael@0: void RestyleAfterPseudo(nsIFrame* aFrame); michael@0: void RestyleContentChildren(nsIFrame* aParent, michael@0: nsRestyleHint aChildRestyleHint); michael@0: void InitializeAccessibilityNotifications(); michael@0: void SendAccessibilityNotifications(); michael@0: michael@0: enum DesiredA11yNotifications { michael@0: eSkipNotifications, michael@0: eSendAllNotifications, michael@0: eNotifyIfShown michael@0: }; michael@0: michael@0: enum A11yNotificationType { michael@0: eDontNotify, michael@0: eNotifyShown, michael@0: eNotifyHidden michael@0: }; michael@0: michael@0: private: michael@0: nsPresContext* const mPresContext; michael@0: nsIFrame* const mFrame; michael@0: nsIContent* const mParentContent; michael@0: // |mContent| is the node that we used for rule matching of michael@0: // normal elements (not pseudo-elements) and for which we generate michael@0: // framechange hints if we need them. michael@0: nsIContent* const mContent; michael@0: nsStyleChangeList* const mChangeList; michael@0: // We have already generated change list entries for hints listed in michael@0: // mHintsHandled (initially it's those handled by ancestors, but by michael@0: // the end of Restyle it is those handled for this frame as well). We michael@0: // need to generate a new change list entry for the frame when its michael@0: // style comparision returns a hint other than one of these hints. michael@0: nsChangeHint mHintsHandled; michael@0: // See nsStyleContext::CalcStyleDifference michael@0: nsChangeHint mParentFrameHintsNotHandledForDescendants; michael@0: nsChangeHint mHintsNotHandledForDescendants; michael@0: RestyleTracker& mRestyleTracker; michael@0: TreeMatchContext& mTreeMatchContext; michael@0: nsIFrame* mResolvedChild; // child that provides our parent style context michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: const DesiredA11yNotifications mDesiredA11yNotifications; michael@0: DesiredA11yNotifications mKidsDesiredA11yNotifications; michael@0: A11yNotificationType mOurA11yNotification; michael@0: nsTArray& mVisibleKidsOfHiddenElement; michael@0: bool mWasFrameVisible; michael@0: #endif michael@0: }; michael@0: michael@0: } // namespace mozilla michael@0: michael@0: #endif /* mozilla_RestyleManager_h */