diff -r 000000000000 -r 6474c204b198 layout/base/RestyleManager.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layout/base/RestyleManager.h Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,405 @@ +/* -*- 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 "nsISupportsImpl.h" +#include "nsChangeHint.h" +#include "RestyleTracker.h" +#include "nsPresContext.h" + +class nsRefreshDriver; +class nsIFrame; +struct TreeMatchContext; + +namespace mozilla { + class EventStates; + +namespace dom { + class Element; +} // namespace dom + +class RestyleManager MOZ_FINAL { +public: + friend class ::nsRefreshDriver; + friend class RestyleTracker; + + typedef mozilla::dom::Element Element; + + RestyleManager(nsPresContext* aPresContext); + +private: + // Private destructor, to discourage deletion outside of Release(): + ~RestyleManager() + { + } + +public: + NS_INLINE_DECL_REFCOUNTING(mozilla::RestyleManager) + + void Disconnect() { + mPresContext = nullptr; + } + + nsPresContext* PresContext() const { + MOZ_ASSERT(mPresContext); + return mPresContext; + } + + nsCSSFrameConstructor* FrameConstructor() const + { return PresContext()->FrameConstructor(); } + + // Should be called when a frame is going to be destroyed and + // WillDestroyFrameTree hasn't been called yet. + void NotifyDestroyingFrame(nsIFrame* aFrame); + + // 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); + // 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); + + // Get an integer that increments every time there is a style change + // as a result of a change to the :hover content state. + uint32_t GetHoverGeneration() const { return mHoverGeneration; } + + // 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; } + + /** + * 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. + */ + NS_HIDDEN_(nsresult) ReparentStyleContext(nsIFrame* aFrame); + + /** + * Re-resolve the style contexts for a frame tree, building + * aChangeList based on the resulting style changes, plus aMinChange + * applied to aFrame. + */ + NS_HIDDEN_(void) + ComputeStyleChangeFor(nsIFrame* aFrame, + nsStyleChangeList* aChangeList, + nsChangeHint aMinChange, + RestyleTracker& aRestyleTracker, + bool aRestyleDescendants); + +#ifdef DEBUG + /** + * DEBUG ONLY method to verify integrity of style tree versus frame tree + */ + NS_HIDDEN_(void) DebugVerifyStyleTree(nsIFrame* aFrame); +#endif + + // Note: It's the caller's responsibility to make sure to wrap a + // ProcessRestyledFrames call in a view update batch and a script blocker. + // This function does not call ProcessAttachedQueue() on the binding manager. + // If the caller wants that to happen synchronously, it needs to handle that + // itself. + nsresult ProcessRestyledFrames(nsStyleChangeList& aRestyleArray); + +private: + void RestyleForEmptyChange(Element* aContainer); + +public: + // 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(Element* aContainer, nsIContent* aChild); + + // This would be the same 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 RestyleForRemove(Element* aContainer, + nsIContent* aOldChild, + nsIContent* aFollowingSibling); + + // Same for a ContentAppended. |aContainer| must be non-null; when + // the container is null, no work is needed. + void RestyleForAppend(Element* 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(); + + // 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(); + void EndProcessingRestyles(); + + // 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. + void RebuildAllStyleData(nsChangeHint aExtraHint); + + // Helper that does part of the work of RebuildAllStyleData, shared by + // RestyleElement for 'rem' handling. + void DoRebuildAllStyleData(RestyleTracker& aRestyleTracker, + nsChangeHint aExtraHint); + + // See PostRestyleEventCommon below. + void PostRestyleEvent(Element* aElement, + nsRestyleHint aRestyleHint, + nsChangeHint aMinChangeHint) + { + if (mPresContext) { + PostRestyleEventCommon(aElement, aRestyleHint, aMinChangeHint, + mPresContext->IsProcessingAnimationStyleChange()); + } + } + + // See PostRestyleEventCommon below. + void PostAnimationRestyleEvent(Element* aElement, + nsRestyleHint aRestyleHint, + nsChangeHint aMinChangeHint) + { + PostRestyleEventCommon(aElement, aRestyleHint, aMinChangeHint, true); + } + + void PostRestyleEventForLazyConstruction() + { + PostRestyleEventInternal(true); + } + + void FlushOverflowChangedTracker() + { + mOverflowChangedTracker.Flush(); + } + +private: + /** + * 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 aForAnimation: Whether the style should be computed with or + * without animation data. Animation code + * sometimes needs to pass true; other code + * should generally pass the the pres context's + * IsProcessingAnimationStyleChange() value + * (which is the default value). + */ + void PostRestyleEventCommon(Element* aElement, + nsRestyleHint aRestyleHint, + nsChangeHint aMinChangeHint, + bool aForAnimation); + void PostRestyleEventInternal(bool aForLazyConstruction); + +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. + */ + void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint); + +private: + /* 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, + bool aRestyleDescendants); + + nsresult StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint); + + // Returns true if this function managed to successfully move a frame, and + // false if it could not process the position change, and a reflow should + // be performed instead. + bool RecomputePosition(nsIFrame* aFrame); + +private: + nsPresContext* mPresContext; // weak, disconnected in Disconnect + + bool mRebuildAllStyleData : 1; + // True if we're already waiting for a refresh notification + bool mObservingRefreshDriver : 1; + // True if we're in the middle of a nsRefreshDriver refresh + bool mInStyleRefresh : 1; + uint32_t mHoverGeneration; + nsChangeHint mRebuildAllExtraHint; + + OverflowChangedTracker mOverflowChangedTracker; + + // The total number of animation flushes by this frame constructor. + // Used to keep the layer and animation manager in sync. + uint64_t mAnimationGeneration; + + RestyleTracker mPendingRestyles; + RestyleTracker mPendingAnimationRestyles; +}; + +/** + * An ElementRestyler is created for *each* element in a subtree that we + * recompute styles for. + */ +class ElementRestyler MOZ_FINAL { +public: + typedef mozilla::dom::Element Element; + + // Construct for the root of the subtree that we're restyling. + ElementRestyler(nsPresContext* aPresContext, + nsIFrame* aFrame, + nsStyleChangeList* aChangeList, + nsChangeHint aHintsHandledByAncestors, + RestyleTracker& aRestyleTracker, + TreeMatchContext& aTreeMatchContext, + nsTArray& aVisibleKidsOfHiddenElement); + + // 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 outer table frame (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); + + /** + * 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; } + +private: + /** + * First half of Restyle(). + */ + void RestyleSelf(nsIFrame* aSelf, nsRestyleHint aRestyleHint); + + /** + * Restyle the children of this frame (and, in turn, their children). + * + * Second half of Restyle(). + */ + void RestyleChildren(nsRestyleHint aChildRestyleHint); + + /** + * Helper for RestyleSelf(). + */ + void CaptureChange(nsStyleContext* aOldContext, + nsStyleContext* aNewContext, + nsChangeHint aChangeToAssume); + + /** + * Helpers for RestyleChildren(). + */ + void RestyleUndisplayedChildren(nsRestyleHint aChildRestyleHint); + void RestyleBeforePseudo(); + void RestyleAfterPseudo(nsIFrame* aFrame); + void RestyleContentChildren(nsIFrame* aParent, + nsRestyleHint aChildRestyleHint); + void InitializeAccessibilityNotifications(); + void SendAccessibilityNotifications(); + + enum DesiredA11yNotifications { + eSkipNotifications, + eSendAllNotifications, + eNotifyIfShown + }; + + enum A11yNotificationType { + eDontNotify, + eNotifyShown, + eNotifyHidden + }; + +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; + TreeMatchContext& mTreeMatchContext; + nsIFrame* mResolvedChild; // child that provides our parent style context + +#ifdef ACCESSIBILITY + const DesiredA11yNotifications mDesiredA11yNotifications; + DesiredA11yNotifications mKidsDesiredA11yNotifications; + A11yNotificationType mOurA11yNotification; + nsTArray& mVisibleKidsOfHiddenElement; + bool mWasFrameVisible; +#endif +}; + +} // namespace mozilla + +#endif /* mozilla_RestyleManager_h */