diff options
Diffstat (limited to 'layout/base/DisplayListClipState.h')
-rw-r--r-- | layout/base/DisplayListClipState.h | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/layout/base/DisplayListClipState.h b/layout/base/DisplayListClipState.h new file mode 100644 index 0000000000..6576ef4102 --- /dev/null +++ b/layout/base/DisplayListClipState.h @@ -0,0 +1,468 @@ +/* -*- Mode: C++; tab-width: 20; 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/. */ + +#ifndef DISPLAYLISTCLIPSTATE_H_ +#define DISPLAYLISTCLIPSTATE_H_ + +#include "DisplayItemClip.h" +#include "DisplayItemScrollClip.h" + +#include "mozilla/DebugOnly.h" + +class nsIFrame; +class nsIScrollableFrame; +class nsDisplayListBuilder; + +namespace mozilla { + +/** + * All clip coordinates are in appunits relative to the reference frame + * for the display item we're building. + */ +class DisplayListClipState { +public: + DisplayListClipState() + : mClipContentDescendants(nullptr) + , mClipContainingBlockDescendants(nullptr) + , mCurrentCombinedClip(nullptr) + , mScrollClipContentDescendants(nullptr) + , mScrollClipContainingBlockDescendants(nullptr) + , mClipContentDescendantsScrollClip(nullptr) + , mStackingContextAncestorSC(nullptr) + {} + + /** + * Returns intersection of mClipContainingBlockDescendants and + * mClipContentDescendants, allocated on aBuilder's arena. + */ + const DisplayItemClip* GetCurrentCombinedClip(nsDisplayListBuilder* aBuilder); + + const DisplayItemClip* GetClipForContainingBlockDescendants() const + { + return mClipContainingBlockDescendants; + } + const DisplayItemClip* GetClipForContentDescendants() const + { + return mClipContentDescendants; + } + + const DisplayItemScrollClip* GetCurrentInnermostScrollClip(); + + const DisplayItemScrollClip* CurrentAncestorScrollClipForStackingContextContents() + { + return mStackingContextAncestorSC; + } + + class AutoSaveRestore; + friend class AutoSaveRestore; + + class AutoClipContainingBlockDescendantsToContentBox; + friend class AutoClipContainingBlockDescendantsToContentBox; + + class AutoClipMultiple; + friend class AutoClipMultiple; + + enum { + ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT = 0x01 + }; + +private: + void SetClipForContainingBlockDescendants(const DisplayItemClip* aClip) + { + mClipContainingBlockDescendants = aClip; + mCurrentCombinedClip = nullptr; + } + + void SetScrollClipForContainingBlockDescendants(nsDisplayListBuilder* aBuilder, + const DisplayItemScrollClip* aScrollClip); + + void Clear() + { + mClipContentDescendants = nullptr; + mClipContainingBlockDescendants = nullptr; + mCurrentCombinedClip = nullptr; + // We do not clear scroll clips. + } + + void EnterStackingContextContents(bool aClear) + { + if (aClear) { + mClipContentDescendants = nullptr; + mClipContainingBlockDescendants = nullptr; + mCurrentCombinedClip = nullptr; + mScrollClipContentDescendants = nullptr; + mScrollClipContainingBlockDescendants = nullptr; + mStackingContextAncestorSC = nullptr; + } else { + mStackingContextAncestorSC = GetCurrentInnermostScrollClip(); + } + } + + /** + * Clear the current clip, and instead add it as a scroll clip to the current + * scroll clip chain. + */ + void TurnClipIntoScrollClipForContentDescendants(nsDisplayListBuilder* aBuilder, + nsIScrollableFrame* aScrollableFrame); + void TurnClipIntoScrollClipForContainingBlockDescendants(nsDisplayListBuilder* aBuilder, + nsIScrollableFrame* aScrollableFrame); + + /** + * Insert a scroll clip without clearing the current clip. + * The returned DisplayItemScrollClip will have mIsAsyncScrollable == false, + * and it can be activated once the scroll frame knows that it needs to be + * async scrollable. + */ + DisplayItemScrollClip* InsertInactiveScrollClipForContentDescendants(nsDisplayListBuilder* aBuilder, + nsIScrollableFrame* aScrollableFrame); + DisplayItemScrollClip* InsertInactiveScrollClipForContainingBlockDescendants(nsDisplayListBuilder* aBuilder, + nsIScrollableFrame* aScrollableFrame); + + DisplayItemScrollClip* CreateInactiveScrollClip(nsDisplayListBuilder* aBuilder, + nsIScrollableFrame* aScrollableFrame); + + /** + * Intersects the given clip rect (with optional aRadii) with the current + * mClipContainingBlockDescendants and sets mClipContainingBlockDescendants to + * the result, stored in aClipOnStack. + */ + void ClipContainingBlockDescendants(const nsRect& aRect, + const nscoord* aRadii, + DisplayItemClip& aClipOnStack); + + void ClipContentDescendants(const nsRect& aRect, + const nscoord* aRadii, + DisplayItemClip& aClipOnStack); + void ClipContentDescendants(const nsRect& aRect, + const nsRect& aRoundedRect, + const nscoord* aRadii, + DisplayItemClip& aClipOnStack); + + /** + * Clips containing-block descendants to the frame's content-box, + * taking border-radius into account. + * If aFlags contains ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT then + * we assume display items will not draw outside the content rect, so + * clipping is only required if there is a border-radius. This is an + * optimization to reduce the amount of clipping required. + */ + void ClipContainingBlockDescendantsToContentBox(nsDisplayListBuilder* aBuilder, + nsIFrame* aFrame, + DisplayItemClip& aClipOnStack, + uint32_t aFlags); + + /** + * All content descendants (i.e. following placeholder frames to their + * out-of-flows if necessary) should be clipped by mClipContentDescendants. + * Null if no clipping applies. + */ + const DisplayItemClip* mClipContentDescendants; + /** + * All containing-block descendants (i.e. frame descendants), including + * display items for the current frame, should be clipped by + * mClipContainingBlockDescendants. + * Null if no clipping applies. + */ + const DisplayItemClip* mClipContainingBlockDescendants; + /** + * The intersection of mClipContentDescendants and + * mClipContainingBlockDescendants. + * Allocated in the nsDisplayListBuilder arena. Null if none has been + * allocated or both mClipContentDescendants and mClipContainingBlockDescendants + * are null. + */ + const DisplayItemClip* mCurrentCombinedClip; + + /** + * The same for scroll clips. + */ + const DisplayItemScrollClip* mScrollClipContentDescendants; + const DisplayItemScrollClip* mScrollClipContainingBlockDescendants; + + /** + * The scroll clip that was in effect when mClipContentDescendants was set. + */ + const DisplayItemScrollClip* mClipContentDescendantsScrollClip; + + /** + * A scroll clip that is an ancestor of all the scroll clips that were + * "current" on this clip state since EnterStackingContextContents was + * called. + */ + const DisplayItemScrollClip* mStackingContextAncestorSC; +}; + +/** + * A class to automatically save and restore the current clip state. Also + * offers methods for modifying the clip state. Only one modification is allowed + * to be in scope at a time using one of these objects; multiple modifications + * require nested objects. The interface is written this way to prevent + * dangling pointers to DisplayItemClips. + */ +class DisplayListClipState::AutoSaveRestore { +public: + explicit AutoSaveRestore(nsDisplayListBuilder* aBuilder); + void Restore() + { + if (!mClearedForStackingContextContents) { + // Forward along the ancestor scroll clip to the original clip state. + mSavedState.mStackingContextAncestorSC = + DisplayItemScrollClip::PickAncestor(mSavedState.mStackingContextAncestorSC, + mState.mStackingContextAncestorSC); + } + mState = mSavedState; +#ifdef DEBUG + mRestored = true; +#endif + } + ~AutoSaveRestore() + { + Restore(); + } + + void Clear() + { + NS_ASSERTION(!mRestored, "Already restored!"); + mState.Clear(); +#ifdef DEBUG + mClipUsed = false; +#endif + } + + void EnterStackingContextContents(bool aClear) + { + NS_ASSERTION(!mRestored, "Already restored!"); + mState.EnterStackingContextContents(aClear); + mClearedForStackingContextContents = aClear; + } + + void ExitStackingContextContents(const DisplayItemScrollClip** aOutContainerSC) + { + if (mClearedForStackingContextContents) { + // If we cleared the scroll clip, then the scroll clip that was current + // just before we cleared it is the one that needs to be set on the + // container item. + *aOutContainerSC = mSavedState.GetCurrentInnermostScrollClip(); + } else { + // If we didn't clear the scroll clip, then the container item needs to + // get a scroll clip that's an ancestor of all its direct child items' + // scroll clips. + // The simplest way to satisfy this requirement would be to just take the + // root scroll clip (i.e. nullptr). However, this can cause the bounds of + // the container items to be enlarged unnecessarily, so instead we try to + // take the "deepest" scroll clip that satisfies the requirement. + // Usually this is the scroll clip that was current before we entered + // the stacking context contents (call that the "initial scroll clip"). + // There are two cases in which the container scroll clip *won't* be the + // initial scroll clip (instead the container scroll clip will be a + // proper ancestor of the initial scroll clip): + // (1) If SetScrollClipForContainingBlockDescendants was called with an + // ancestor scroll clip of the initial scroll clip while we were + // building our direct child items. This happens if we entered a + // position:absolute or position:fixed element whose containing + // block is an ancestor of the frame that generated the initial + // scroll clip. Then the "ancestor scroll clip for stacking context + // contents" will be set to that scroll clip. + // (2) If one of our direct child items is a container item for which + // (1) or (2) happened. + *aOutContainerSC = mState.CurrentAncestorScrollClipForStackingContextContents(); + } + Restore(); + } + + bool SavedStateHasRoundedCorners() + { + const DisplayItemScrollClip* scrollClip = mSavedState.GetCurrentInnermostScrollClip(); + if (scrollClip && scrollClip->HasRoundedCorners()) { + return true; + } + const DisplayItemClip* clip = mSavedState.GetClipForContainingBlockDescendants(); + if (clip && clip->GetRoundedRectCount() > 0) { + return true; + } + + clip = mSavedState.GetClipForContentDescendants(); + if (clip && clip->GetRoundedRectCount() > 0) { + return true; + } + return false; + } + + void TurnClipIntoScrollClipForContentDescendants(nsDisplayListBuilder* aBuilder, nsIScrollableFrame* aScrollableFrame) + { + NS_ASSERTION(!mRestored, "Already restored!"); + mState.TurnClipIntoScrollClipForContentDescendants(aBuilder, aScrollableFrame); +#ifdef DEBUG + mClipUsed = true; +#endif + } + + void TurnClipIntoScrollClipForContainingBlockDescendants(nsDisplayListBuilder* aBuilder, nsIScrollableFrame* aScrollableFrame) + { + NS_ASSERTION(!mRestored, "Already restored!"); + mState.TurnClipIntoScrollClipForContainingBlockDescendants(aBuilder, aScrollableFrame); +#ifdef DEBUG + mClipUsed = true; +#endif + } + + DisplayItemScrollClip* InsertInactiveScrollClipForContentDescendants(nsDisplayListBuilder* aBuilder, nsIScrollableFrame* aScrollableFrame) + { + NS_ASSERTION(!mRestored, "Already restored!"); + DisplayItemScrollClip* scrollClip = mState.InsertInactiveScrollClipForContentDescendants(aBuilder, aScrollableFrame); +#ifdef DEBUG + mClipUsed = true; +#endif + return scrollClip; + } + + DisplayItemScrollClip* InsertInactiveScrollClipForContainingBlockDescendants(nsDisplayListBuilder* aBuilder, nsIScrollableFrame* aScrollableFrame) + { + NS_ASSERTION(!mRestored, "Already restored!"); + DisplayItemScrollClip* scrollClip = mState.InsertInactiveScrollClipForContainingBlockDescendants(aBuilder, aScrollableFrame); +#ifdef DEBUG + mClipUsed = true; +#endif + return scrollClip; + } + + /** + * Intersects the given clip rect (with optional aRadii) with the current + * mClipContainingBlockDescendants and sets mClipContainingBlockDescendants to + * the result, stored in aClipOnStack. + */ + void ClipContainingBlockDescendants(const nsRect& aRect, + const nscoord* aRadii = nullptr) + { + NS_ASSERTION(!mRestored, "Already restored!"); + NS_ASSERTION(!mClipUsed, "mClip already used"); +#ifdef DEBUG + mClipUsed = true; +#endif + mState.ClipContainingBlockDescendants(aRect, aRadii, mClip); + } + + void ClipContentDescendants(const nsRect& aRect, + const nscoord* aRadii = nullptr) + { + NS_ASSERTION(!mRestored, "Already restored!"); + NS_ASSERTION(!mClipUsed, "mClip already used"); +#ifdef DEBUG + mClipUsed = true; +#endif + mState.ClipContentDescendants(aRect, aRadii, mClip); + } + + void ClipContentDescendants(const nsRect& aRect, + const nsRect& aRoundedRect, + const nscoord* aRadii = nullptr) + { + NS_ASSERTION(!mRestored, "Already restored!"); + NS_ASSERTION(!mClipUsed, "mClip already used"); +#ifdef DEBUG + mClipUsed = true; +#endif + mState.ClipContentDescendants(aRect, aRoundedRect, aRadii, mClip); + } + + /** + * Clips containing-block descendants to the frame's content-box, + * taking border-radius into account. + * If aFlags contains ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT then + * we assume display items will not draw outside the content rect, so + * clipping is only required if there is a border-radius. This is an + * optimization to reduce the amount of clipping required. + */ + void ClipContainingBlockDescendantsToContentBox(nsDisplayListBuilder* aBuilder, + nsIFrame* aFrame, + uint32_t aFlags = 0) + { + NS_ASSERTION(!mRestored, "Already restored!"); + NS_ASSERTION(!mClipUsed, "mClip already used"); +#ifdef DEBUG + mClipUsed = true; +#endif + mState.ClipContainingBlockDescendantsToContentBox(aBuilder, aFrame, mClip, aFlags); + } + +protected: + DisplayListClipState& mState; + DisplayListClipState mSavedState; + DisplayItemClip mClip; +#ifdef DEBUG + bool mClipUsed; + bool mRestored; +#endif + bool mClearedForStackingContextContents; +}; + +class DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox : public AutoSaveRestore { +public: + AutoClipContainingBlockDescendantsToContentBox(nsDisplayListBuilder* aBuilder, + nsIFrame* aFrame, + uint32_t aFlags = 0) + : AutoSaveRestore(aBuilder) + { +#ifdef DEBUG + mClipUsed = true; +#endif + mState.ClipContainingBlockDescendantsToContentBox(aBuilder, aFrame, mClip, aFlags); + } +}; + +/** + * Do not use this outside of nsFrame::BuildDisplayListForChild, use + * multiple AutoSaveRestores instead. We provide this class just to ensure + * BuildDisplayListForChild is as efficient as possible. + */ +class DisplayListClipState::AutoClipMultiple : public AutoSaveRestore { +public: + explicit AutoClipMultiple(nsDisplayListBuilder* aBuilder) + : AutoSaveRestore(aBuilder) +#ifdef DEBUG + , mExtraClipUsed(false) +#endif + {} + + /** + * *aClip must survive longer than this object. Be careful!!! + */ + void SetClipForContainingBlockDescendants(const DisplayItemClip* aClip) + { + mState.SetClipForContainingBlockDescendants(aClip); + } + + void SetScrollClipForContainingBlockDescendants(nsDisplayListBuilder* aBuilder, + const DisplayItemScrollClip* aScrollClip) + { + mState.SetScrollClipForContainingBlockDescendants(aBuilder, aScrollClip); + } + + /** + * Intersects the given clip rect (with optional aRadii) with the current + * mClipContainingBlockDescendants and sets mClipContainingBlockDescendants to + * the result, stored in aClipOnStack. + */ + void ClipContainingBlockDescendantsExtra(const nsRect& aRect, + const nscoord* aRadii) + { + NS_ASSERTION(!mRestored, "Already restored!"); + NS_ASSERTION(!mExtraClipUsed, "mExtraClip already used"); +#ifdef DEBUG + mExtraClipUsed = true; +#endif + mState.ClipContainingBlockDescendants(aRect, aRadii, mExtraClip); + } + +protected: + DisplayItemClip mExtraClip; +#ifdef DEBUG + bool mExtraClipUsed; +#endif +}; + +} // namespace mozilla + +#endif /* DISPLAYLISTCLIPSTATE_H_ */ |