Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "mozilla/layers/ScrollingLayersHelper.h"
7 :
8 : #include "FrameMetrics.h"
9 : #include "mozilla/layers/StackingContextHelper.h"
10 : #include "mozilla/layers/WebRenderLayer.h"
11 : #include "mozilla/layers/WebRenderLayerManager.h"
12 : #include "mozilla/webrender/WebRenderAPI.h"
13 : #include "UnitTransforms.h"
14 :
15 : namespace mozilla {
16 : namespace layers {
17 :
18 0 : ScrollingLayersHelper::ScrollingLayersHelper(WebRenderLayer* aLayer,
19 : wr::DisplayListBuilder& aBuilder,
20 0 : const StackingContextHelper& aStackingContext)
21 : : mLayer(aLayer)
22 : , mBuilder(&aBuilder)
23 0 : , mPushedLayerLocalClip(false)
24 : {
25 0 : if (!mLayer->WrManager()->AsyncPanZoomEnabled()) {
26 : // If APZ is disabled then we don't need to push the scrolling clips. We
27 : // still want to push the layer's local clip though.
28 0 : PushLayerLocalClip(aStackingContext);
29 0 : return;
30 : }
31 :
32 0 : Layer* layer = mLayer->GetLayer();
33 0 : for (uint32_t i = layer->GetScrollMetadataCount(); i > 0; i--) {
34 0 : const ScrollMetadata& metadata = layer->GetScrollMetadata(i - 1);
35 : // The scroll clip on a given metadata is affected by all async transforms
36 : // from metadatas "above" it, but not the async transform on the metadata
37 : // itself. Therefore we need to push this clip before we push the
38 : // corresponding scroll layer, so that when we set an async scroll position
39 : // on the scroll layer, the clip isn't affected by it.
40 0 : if (const Maybe<LayerClip>& clip = metadata.GetScrollClip()) {
41 0 : PushLayerClip(clip.ref(), aStackingContext);
42 : }
43 :
44 0 : const FrameMetrics& fm = layer->GetFrameMetrics(i - 1);
45 0 : if (layer->GetIsFixedPosition() &&
46 0 : layer->GetFixedPositionScrollContainerId() == fm.GetScrollId()) {
47 : // If the layer contents are fixed for this metadata onwards, we need
48 : // to insert the layer's local clip at this point in the clip tree,
49 : // as a child of whatever's on the stack.
50 0 : PushLayerLocalClip(aStackingContext);
51 : }
52 :
53 0 : if (!fm.IsScrollable()) {
54 0 : continue;
55 : }
56 : LayerRect contentRect = ViewAs<LayerPixel>(
57 0 : fm.GetExpandedScrollableRect() * fm.GetDevPixelsPerCSSPixel(),
58 0 : PixelCastJustification::WebRenderHasUnitResolution);
59 : // TODO: check coordinate systems are sane here
60 : LayerRect clipBounds = ViewAs<LayerPixel>(
61 0 : fm.GetCompositionBounds(),
62 0 : PixelCastJustification::MovingDownToChildren);
63 : // The content rect that we hand to PushScrollLayer should be relative to
64 : // the same origin as the clipBounds that we hand to PushScrollLayer - that
65 : // is, both of them should be relative to the stacking context `aStackingContext`.
66 : // However, when we get the scrollable rect from the FrameMetrics, the origin
67 : // has nothing to do with the position of the frame but instead represents
68 : // the minimum allowed scroll offset of the scrollable content. While APZ
69 : // uses this to clamp the scroll position, we don't need to send this to
70 : // WebRender at all. Instead, we take the position from the composition
71 : // bounds.
72 0 : contentRect.MoveTo(clipBounds.TopLeft());
73 0 : mBuilder->PushScrollLayer(fm.GetScrollId(),
74 0 : aStackingContext.ToRelativeWrRect(contentRect),
75 0 : aStackingContext.ToRelativeWrRect(clipBounds));
76 : }
77 :
78 : // The scrolled clip on the layer is "inside" all of the scrollable metadatas
79 : // on that layer. That is, the clip scrolls along with the content in
80 : // child layers. So we need to apply this after pushing all the scroll layers,
81 : // which we do above.
82 0 : if (const Maybe<LayerClip>& scrolledClip = layer->GetScrolledClip()) {
83 0 : PushLayerClip(scrolledClip.ref(), aStackingContext);
84 : }
85 :
86 : // If the layer is marked as fixed-position, it is fixed relative to something
87 : // (the scroll layer referred to by GetFixedPositionScrollContainerId, hereafter
88 : // referred to as the "scroll container"). What this really means is that we
89 : // don't want this content to scroll with any scroll layer on the stack up to
90 : // and including the scroll container, but we do want it to scroll with any
91 : // ancestor scroll layers.
92 : // Also, the local clip on the layer (defined by layer->GetClipRect() and
93 : // layer->GetMaskLayer()) also need to be fixed relative to the scroll
94 : // container. This is why we inserted it into the clip tree during the
95 : // loop above when we encountered the scroll container.
96 : // At this point we do a PushClipAndScrollInfo that maintains
97 : // the current non-scrolling clip stack, but resets the scrolling clip stack
98 : // to the ancestor of the scroll container.
99 0 : if (layer->GetIsFixedPosition()) {
100 0 : FrameMetrics::ViewID fixedFor = layer->GetFixedPositionScrollContainerId();
101 0 : Maybe<FrameMetrics::ViewID> scrollsWith = mBuilder->ParentScrollIdFor(fixedFor);
102 0 : Maybe<wr::WrClipId> clipId = mBuilder->TopmostClipId();
103 : // Default to 0 if there is no ancestor, because 0 refers to the root scrollframe.
104 0 : mBuilder->PushClipAndScrollInfo(scrollsWith.valueOr(0), clipId.ptrOr(nullptr));
105 : } else {
106 0 : PushLayerLocalClip(aStackingContext);
107 : }
108 : }
109 :
110 : void
111 0 : ScrollingLayersHelper::PushLayerLocalClip(const StackingContextHelper& aStackingContext)
112 : {
113 0 : Layer* layer = mLayer->GetLayer();
114 0 : Maybe<ParentLayerRect> clip;
115 0 : if (const Maybe<ParentLayerIntRect>& rect = layer->GetClipRect()) {
116 0 : clip = Some(IntRectToRect(rect.ref()));
117 0 : } else if (layer->GetMaskLayer()) {
118 : // this layer has a mask, but no clip rect. so let's use the transformed
119 : // visible bounds as the clip rect.
120 0 : clip = Some(layer->GetLocalTransformTyped().TransformBounds(mLayer->Bounds()));
121 : }
122 0 : if (clip) {
123 0 : Maybe<WrImageMask> mask = mLayer->BuildWrMaskLayer(aStackingContext);
124 0 : LayerRect clipRect = ViewAs<LayerPixel>(clip.ref(),
125 0 : PixelCastJustification::MovingDownToChildren);
126 0 : mBuilder->PushClip(aStackingContext.ToRelativeWrRect(clipRect), mask.ptrOr(nullptr));
127 0 : mPushedLayerLocalClip = true;
128 : }
129 0 : }
130 :
131 : void
132 0 : ScrollingLayersHelper::PushLayerClip(const LayerClip& aClip,
133 : const StackingContextHelper& aSc)
134 : {
135 0 : LayerRect clipRect = IntRectToRect(ViewAs<LayerPixel>(aClip.GetClipRect(),
136 0 : PixelCastJustification::MovingDownToChildren));
137 0 : Maybe<WrImageMask> mask;
138 0 : if (Maybe<size_t> maskLayerIndex = aClip.GetMaskLayerIndex()) {
139 0 : Layer* maskLayer = mLayer->GetLayer()->GetAncestorMaskLayerAt(maskLayerIndex.value());
140 0 : WebRenderLayer* maskWrLayer = WebRenderLayer::ToWebRenderLayer(maskLayer);
141 : // TODO: check this transform is correct in all cases
142 0 : mask = maskWrLayer->RenderMaskLayer(aSc, maskLayer->GetTransform());
143 : }
144 0 : mBuilder->PushClip(aSc.ToRelativeWrRect(clipRect), mask.ptrOr(nullptr));
145 0 : }
146 :
147 0 : ScrollingLayersHelper::~ScrollingLayersHelper()
148 : {
149 0 : Layer* layer = mLayer->GetLayer();
150 0 : if (!mLayer->WrManager()->AsyncPanZoomEnabled()) {
151 0 : if (mPushedLayerLocalClip) {
152 0 : mBuilder->PopClip();
153 : }
154 0 : return;
155 : }
156 :
157 0 : if (layer->GetIsFixedPosition()) {
158 0 : mBuilder->PopClipAndScrollInfo();
159 0 : } else if (mPushedLayerLocalClip) {
160 0 : mBuilder->PopClip();
161 : }
162 0 : if (layer->GetScrolledClip()) {
163 0 : mBuilder->PopClip();
164 : }
165 0 : for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
166 0 : const FrameMetrics& fm = layer->GetFrameMetrics(i);
167 0 : if (fm.IsScrollable()) {
168 0 : mBuilder->PopScrollLayer();
169 : }
170 0 : if (layer->GetIsFixedPosition() &&
171 0 : layer->GetFixedPositionScrollContainerId() == fm.GetScrollId() &&
172 0 : mPushedLayerLocalClip) {
173 0 : mBuilder->PopClip();
174 : }
175 0 : const ScrollMetadata& metadata = layer->GetScrollMetadata(i);
176 0 : if (metadata.GetScrollClip()) {
177 0 : mBuilder->PopClip();
178 : }
179 : }
180 0 : }
181 :
182 : } // namespace layers
183 : } // namespace mozilla
|