summaryrefslogtreecommitdiffstats
path: root/layout/painting/DisplayListClipState.cpp
blob: 8a87e2906b8572b0c4cb48b55bde0a2a58590189 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */

#include "DisplayListClipState.h"

#include "nsDisplayList.h"

namespace mozilla {

const DisplayItemClipChain* DisplayListClipState::GetCurrentCombinedClipChain(
    nsDisplayListBuilder* aBuilder) {
  if (mCurrentCombinedClipChainIsValid) {
    return mCurrentCombinedClipChain;
  }
  if (!mClipChainContentDescendants && !mClipChainContainingBlockDescendants) {
    mCurrentCombinedClipChain = nullptr;
    mCurrentCombinedClipChainIsValid = true;
    return nullptr;
  }

  mCurrentCombinedClipChain = aBuilder->CreateClipChainIntersection(
      mCurrentCombinedClipChain, mClipChainContentDescendants,
      mClipChainContainingBlockDescendants);
  mCurrentCombinedClipChainIsValid = true;
  return mCurrentCombinedClipChain;
}

static void ApplyClip(nsDisplayListBuilder* aBuilder,
                      const DisplayItemClipChain*& aClipToModify,
                      const ActiveScrolledRoot* aASR,
                      DisplayItemClipChain& aClipChainOnStack) {
  aClipChainOnStack.mASR = aASR;
  if (aClipToModify && aClipToModify->mASR == aASR) {
    // Intersect with aClipToModify and replace the clip chain item.
    aClipChainOnStack.mClip.IntersectWith(aClipToModify->mClip);
    aClipChainOnStack.mParent = aClipToModify->mParent;
    aClipToModify = &aClipChainOnStack;
  } else if (!aClipToModify ||
             ActiveScrolledRoot::IsAncestor(aClipToModify->mASR, aASR)) {
    // Add a new clip chain item at the bottom.
    aClipChainOnStack.mParent = aClipToModify;
    aClipToModify = &aClipChainOnStack;
  } else {
    // We need to insert / intersect a DisplayItemClipChain in the middle of the
    // aClipToModify chain. This is a very rare case.
    // Find the common ancestor and have the builder create the
    // DisplayItemClipChain intersection. This will create new
    // DisplayItemClipChain objects for all descendants of ancestorSC and we
    // will not hold on to a pointer to aClipChainOnStack.
    const DisplayItemClipChain* ancestorSC = aClipToModify;
    while (ancestorSC &&
           ActiveScrolledRoot::IsAncestor(aASR, ancestorSC->mASR)) {
      ancestorSC = ancestorSC->mParent;
    }
    ancestorSC = aBuilder->CopyWholeChain(ancestorSC);
    aClipChainOnStack.mParent = nullptr;
    aClipToModify = aBuilder->CreateClipChainIntersection(
        ancestorSC, aClipToModify, &aClipChainOnStack);
  }
}

void DisplayListClipState::ClipContainingBlockDescendants(
    nsDisplayListBuilder* aBuilder, const nsRect& aRect, const nscoord* aRadii,
    DisplayItemClipChain& aClipChainOnStack) {
  if (aRadii) {
    aClipChainOnStack.mClip.SetTo(aRect, aRadii);
  } else {
    aClipChainOnStack.mClip.SetTo(aRect);
  }
  const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
  ApplyClip(aBuilder, mClipChainContainingBlockDescendants, asr,
            aClipChainOnStack);
  InvalidateCurrentCombinedClipChain(asr);
}

void DisplayListClipState::ClipContentDescendants(
    nsDisplayListBuilder* aBuilder, const nsRect& aRect, const nscoord* aRadii,
    DisplayItemClipChain& aClipChainOnStack) {
  if (aRadii) {
    aClipChainOnStack.mClip.SetTo(aRect, aRadii);
  } else {
    aClipChainOnStack.mClip.SetTo(aRect);
  }
  const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
  ApplyClip(aBuilder, mClipChainContentDescendants, asr, aClipChainOnStack);
  InvalidateCurrentCombinedClipChain(asr);
}

void DisplayListClipState::ClipContentDescendants(
    nsDisplayListBuilder* aBuilder, const nsRect& aRect,
    const nsRect& aRoundedRect, const nscoord* aRadii,
    DisplayItemClipChain& aClipChainOnStack) {
  if (aRadii) {
    aClipChainOnStack.mClip.SetTo(aRect, aRoundedRect, aRadii);
  } else {
    nsRect intersect = aRect.Intersect(aRoundedRect);
    aClipChainOnStack.mClip.SetTo(intersect);
  }
  const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
  ApplyClip(aBuilder, mClipChainContentDescendants, asr, aClipChainOnStack);
  InvalidateCurrentCombinedClipChain(asr);
}

void DisplayListClipState::InvalidateCurrentCombinedClipChain(
    const ActiveScrolledRoot* aInvalidateUpTo) {
  mClippedToDisplayPort = false;
  mCurrentCombinedClipChainIsValid = false;
  while (mCurrentCombinedClipChain &&
         ActiveScrolledRoot::IsAncestor(aInvalidateUpTo,
                                        mCurrentCombinedClipChain->mASR)) {
    mCurrentCombinedClipChain = mCurrentCombinedClipChain->mParent;
  }
}

void DisplayListClipState::ClipContainingBlockDescendantsToContentBox(
    nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
    DisplayItemClipChain& aClipChainOnStack, uint32_t aFlags) {
  nscoord radii[8];
  bool hasBorderRadius = aFrame->GetContentBoxBorderRadii(radii);
  if (!hasBorderRadius &&
      (aFlags & ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT)) {
    return;
  }

  nsRect clipRect = aFrame->GetContentRectRelativeToSelf() +
                    aBuilder->ToReferenceFrame(aFrame);
  // If we have a border-radius, we have to clip our content to that
  // radius.
  ClipContainingBlockDescendants(
      aBuilder, clipRect, hasBorderRadius ? radii : nullptr, aClipChainOnStack);
}

DisplayListClipState::AutoSaveRestore::AutoSaveRestore(
    nsDisplayListBuilder* aBuilder)
    : mBuilder(aBuilder),
      mState(aBuilder->ClipState()),
      mSavedState(aBuilder->ClipState())
#ifdef DEBUG
      ,
      mClipUsed(false),
      mRestored(false)
#endif
{
}

}  // namespace mozilla