summaryrefslogtreecommitdiffstats
path: root/layout/base/DisplayListClipState.h
diff options
context:
space:
mode:
Diffstat (limited to 'layout/base/DisplayListClipState.h')
-rw-r--r--layout/base/DisplayListClipState.h468
1 files changed, 468 insertions, 0 deletions
diff --git a/layout/base/DisplayListClipState.h b/layout/base/DisplayListClipState.h
new file mode 100644
index 000000000..6576ef410
--- /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_ */