1 | /*
|
---|
2 | * Copyright (C) 2019 Apple Inc. All rights reserved.
|
---|
3 | *
|
---|
4 | * Redistribution and use in source and binary forms, with or without
|
---|
5 | * modification, are permitted provided that the following conditions
|
---|
6 | * are met:
|
---|
7 | * 1. Redistributions of source code must retain the above copyright
|
---|
8 | * notice, this list of conditions and the following disclaimer.
|
---|
9 | * 2. Redistributions in binary form must reproduce the above copyright
|
---|
10 | * notice, this list of conditions and the following disclaimer in the
|
---|
11 | * documentation and/or other materials provided with the distribution.
|
---|
12 | *
|
---|
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
---|
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
---|
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
---|
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
---|
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
---|
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
---|
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
---|
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
---|
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
---|
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
---|
23 | * THE POSSIBILITY OF SUCH DAMAGE.
|
---|
24 | */
|
---|
25 |
|
---|
26 | #include "config.h"
|
---|
27 | #include "LayerOverlapMap.h"
|
---|
28 | #include "Logging.h"
|
---|
29 | #include "RenderLayer.h"
|
---|
30 | #include <wtf/text/TextStream.h>
|
---|
31 |
|
---|
32 | namespace WebCore {
|
---|
33 |
|
---|
34 | struct RectList {
|
---|
35 | Vector<LayoutRect> rects;
|
---|
36 | LayoutRect boundingRect;
|
---|
37 |
|
---|
38 | void append(const LayoutRect& rect)
|
---|
39 | {
|
---|
40 | rects.append(rect);
|
---|
41 | boundingRect.unite(rect);
|
---|
42 | }
|
---|
43 |
|
---|
44 | void append(const RectList& rectList)
|
---|
45 | {
|
---|
46 | rects.appendVector(rectList.rects);
|
---|
47 | boundingRect.unite(rectList.boundingRect);
|
---|
48 | }
|
---|
49 |
|
---|
50 | bool intersects(const LayoutRect& rect) const
|
---|
51 | {
|
---|
52 | if (!rects.size() || !rect.intersects(boundingRect))
|
---|
53 | return false;
|
---|
54 |
|
---|
55 | for (const auto& currentRect : rects) {
|
---|
56 | if (currentRect.intersects(rect))
|
---|
57 | return true;
|
---|
58 | }
|
---|
59 | return false;
|
---|
60 | }
|
---|
61 | };
|
---|
62 |
|
---|
63 | static TextStream& operator<<(TextStream& ts, const RectList& rectList)
|
---|
64 | {
|
---|
65 | ts << "bounds " << rectList.boundingRect << " (" << rectList.rects << " rects)";
|
---|
66 | return ts;
|
---|
67 | }
|
---|
68 |
|
---|
69 | // Used to store overlap rects in a way that takes overflow into account.
|
---|
70 | // It stores a tree whose nodes are layers with composited scrolling. The tree is built lazily as layers are added whose containing block
|
---|
71 | // chains contain composited scrollers. The tree always starts at the root layer.
|
---|
72 | // Checking for overlap involves finding the node for the clipping layer enclosing the given layer (or the root),
|
---|
73 | // and comparing against the bounds of earlier siblings.
|
---|
74 | class OverlapMapContainer {
|
---|
75 | WTF_MAKE_FAST_ALLOCATED;
|
---|
76 | public:
|
---|
77 | OverlapMapContainer(const RenderLayer& rootLayer, const RenderLayer& scopeLayer)
|
---|
78 | : m_rootScope(rootLayer)
|
---|
79 | , m_scopeLayer(scopeLayer)
|
---|
80 | {
|
---|
81 | }
|
---|
82 |
|
---|
83 | // Layers are added in z-order, lazily creating clipping scopes as necessary.
|
---|
84 | void add(const RenderLayer&, const LayoutRect& bounds, const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers);
|
---|
85 | bool overlapsLayers(const RenderLayer&, const LayoutRect& bounds, const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers) const;
|
---|
86 | void append(std::unique_ptr<OverlapMapContainer>&&);
|
---|
87 |
|
---|
88 | String dump(unsigned) const;
|
---|
89 |
|
---|
90 | const RenderLayer& scopeLayer() const { return m_scopeLayer; }
|
---|
91 |
|
---|
92 | private:
|
---|
93 | struct ClippingScope {
|
---|
94 | ClippingScope(const RenderLayer& inLayer)
|
---|
95 | : layer(inLayer)
|
---|
96 | {
|
---|
97 | }
|
---|
98 |
|
---|
99 | ClippingScope(const LayerOverlapMap::LayerAndBounds& layerAndBounds)
|
---|
100 | : layer(layerAndBounds.layer)
|
---|
101 | , bounds(layerAndBounds.bounds)
|
---|
102 | {
|
---|
103 | }
|
---|
104 |
|
---|
105 | ClippingScope* childWithLayer(const RenderLayer& layer) const
|
---|
106 | {
|
---|
107 | for (auto& child : children) {
|
---|
108 | if (&child.layer == &layer)
|
---|
109 | return const_cast<ClippingScope*>(&child);
|
---|
110 | }
|
---|
111 | return nullptr;
|
---|
112 | }
|
---|
113 |
|
---|
114 | ClippingScope* addChildWithLayerAndBounds(const LayerOverlapMap::LayerAndBounds& layerAndBounds)
|
---|
115 | {
|
---|
116 | children.append({ layerAndBounds });
|
---|
117 | return &children.last();
|
---|
118 | }
|
---|
119 |
|
---|
120 | ClippingScope* addChild(const ClippingScope& child)
|
---|
121 | {
|
---|
122 | ASSERT(&layer != &child.layer);
|
---|
123 | children.append(child);
|
---|
124 | return &children.last();
|
---|
125 | }
|
---|
126 |
|
---|
127 | void appendRect(const LayoutRect& bounds)
|
---|
128 | {
|
---|
129 | rectList.append(bounds);
|
---|
130 | }
|
---|
131 |
|
---|
132 | const RenderLayer& layer;
|
---|
133 | LayoutRect bounds; // Bounds of the composited clip.
|
---|
134 | Vector<ClippingScope> children;
|
---|
135 | RectList rectList;
|
---|
136 | };
|
---|
137 |
|
---|
138 | static ClippingScope* clippingScopeContainingLayerChildRecursive(const ClippingScope& currNode, const RenderLayer& layer)
|
---|
139 | {
|
---|
140 | for (auto& child : currNode.children) {
|
---|
141 | if (&layer == &child.layer)
|
---|
142 | return const_cast<ClippingScope*>(&currNode);
|
---|
143 |
|
---|
144 | if (auto* foundNode = clippingScopeContainingLayerChildRecursive(child, layer))
|
---|
145 | return foundNode;
|
---|
146 | }
|
---|
147 |
|
---|
148 | return nullptr;
|
---|
149 | }
|
---|
150 |
|
---|
151 | ClippingScope* scopeContainingLayer(const RenderLayer& layer) const
|
---|
152 | {
|
---|
153 | return clippingScopeContainingLayerChildRecursive(m_rootScope, layer);
|
---|
154 | }
|
---|
155 |
|
---|
156 | static void mergeClippingScopesRecursive(const ClippingScope& sourceScope, ClippingScope& destScope);
|
---|
157 |
|
---|
158 | ClippingScope* ensureClippingScopeForLayers(const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers);
|
---|
159 | ClippingScope* findClippingScopeForLayers(const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers) const;
|
---|
160 |
|
---|
161 | void recursiveOutputToStream(TextStream&, const ClippingScope&, unsigned depth) const;
|
---|
162 |
|
---|
163 | const ClippingScope& rootScope() const { return m_rootScope; }
|
---|
164 | ClippingScope& rootScope() { return m_rootScope; }
|
---|
165 |
|
---|
166 | ClippingScope m_rootScope;
|
---|
167 | const RenderLayer& m_scopeLayer;
|
---|
168 | };
|
---|
169 |
|
---|
170 | void OverlapMapContainer::add(const RenderLayer&, const LayoutRect& bounds, const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers)
|
---|
171 | {
|
---|
172 | auto* layerScope = ensureClippingScopeForLayers(enclosingClippingLayers);
|
---|
173 | layerScope->appendRect(bounds);
|
---|
174 | }
|
---|
175 |
|
---|
176 | bool OverlapMapContainer::overlapsLayers(const RenderLayer&, const LayoutRect& bounds, const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers) const
|
---|
177 | {
|
---|
178 | if (m_rootScope.rectList.intersects(bounds))
|
---|
179 | return true;
|
---|
180 |
|
---|
181 | if (m_rootScope.children.isEmpty())
|
---|
182 | return false;
|
---|
183 |
|
---|
184 | // Find the ClippingScope for which this layer is a child.
|
---|
185 | auto* clippingScope = findClippingScopeForLayers(enclosingClippingLayers);
|
---|
186 | if (!clippingScope)
|
---|
187 | return false;
|
---|
188 |
|
---|
189 | if (clippingScope->rectList.intersects(bounds))
|
---|
190 | return true;
|
---|
191 |
|
---|
192 | // FIXME: In some cases do we have to walk up the ancestor clipping scope chain?
|
---|
193 | return false;
|
---|
194 | }
|
---|
195 |
|
---|
196 | void OverlapMapContainer::mergeClippingScopesRecursive(const ClippingScope& sourceScope, ClippingScope& destScope)
|
---|
197 | {
|
---|
198 | ASSERT(&sourceScope.layer == &destScope.layer);
|
---|
199 | destScope.rectList.append(sourceScope.rectList);
|
---|
200 |
|
---|
201 | for (auto& sourceChildScope : sourceScope.children) {
|
---|
202 | ClippingScope* destChild = destScope.childWithLayer(sourceChildScope.layer);
|
---|
203 | if (destChild)
|
---|
204 | mergeClippingScopesRecursive(sourceChildScope, *destChild);
|
---|
205 | else {
|
---|
206 | // New child, just copy the whole subtree.
|
---|
207 | destScope.addChild(sourceChildScope);
|
---|
208 | }
|
---|
209 | }
|
---|
210 | }
|
---|
211 |
|
---|
212 | void OverlapMapContainer::append(std::unique_ptr<OverlapMapContainer>&& otherContainer)
|
---|
213 | {
|
---|
214 | mergeClippingScopesRecursive(otherContainer->rootScope(), m_rootScope);
|
---|
215 | }
|
---|
216 |
|
---|
217 | OverlapMapContainer::ClippingScope* OverlapMapContainer::ensureClippingScopeForLayers(const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers)
|
---|
218 | {
|
---|
219 | ASSERT(enclosingClippingLayers.size());
|
---|
220 | ASSERT(enclosingClippingLayers[0].layer.isRenderViewLayer());
|
---|
221 |
|
---|
222 | auto* currScope = &m_rootScope;
|
---|
223 | for (unsigned i = 1; i < enclosingClippingLayers.size(); ++i) {
|
---|
224 | auto& scopeLayerAndBounds = enclosingClippingLayers[i];
|
---|
225 | auto* childScope = currScope->childWithLayer(scopeLayerAndBounds.layer);
|
---|
226 | if (!childScope) {
|
---|
227 | currScope = currScope->addChildWithLayerAndBounds(scopeLayerAndBounds);
|
---|
228 | break;
|
---|
229 | }
|
---|
230 |
|
---|
231 | currScope = childScope;
|
---|
232 | }
|
---|
233 |
|
---|
234 | return const_cast<ClippingScope*>(currScope);
|
---|
235 | }
|
---|
236 |
|
---|
237 | OverlapMapContainer::ClippingScope* OverlapMapContainer::findClippingScopeForLayers(const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers) const
|
---|
238 | {
|
---|
239 | ASSERT(enclosingClippingLayers.size());
|
---|
240 | ASSERT(enclosingClippingLayers[0].layer.isRenderViewLayer());
|
---|
241 |
|
---|
242 | const auto* currScope = &m_rootScope;
|
---|
243 | for (unsigned i = 1; i < enclosingClippingLayers.size(); ++i) {
|
---|
244 | auto& scopeLayerAndBounds = enclosingClippingLayers[i];
|
---|
245 | auto* childScope = currScope->childWithLayer(scopeLayerAndBounds.layer);
|
---|
246 | if (!childScope)
|
---|
247 | return nullptr;
|
---|
248 |
|
---|
249 | currScope = childScope;
|
---|
250 | }
|
---|
251 |
|
---|
252 | return const_cast<ClippingScope*>(currScope);
|
---|
253 | }
|
---|
254 |
|
---|
255 | void OverlapMapContainer::recursiveOutputToStream(TextStream& ts, const ClippingScope& scope, unsigned depth) const
|
---|
256 | {
|
---|
257 | ts << "\n" << indent << TextStream::Repeat { 2 * depth, ' ' } << " scope for layer " << &scope.layer << " rects " << scope.rectList;
|
---|
258 | for (auto& childScope : scope.children)
|
---|
259 | recursiveOutputToStream(ts, childScope, depth + 1);
|
---|
260 | }
|
---|
261 |
|
---|
262 | String OverlapMapContainer::dump(unsigned indent) const
|
---|
263 | {
|
---|
264 | TextStream multilineStream;
|
---|
265 | multilineStream.increaseIndent(indent);
|
---|
266 | multilineStream << "overlap container - root layer " << &m_rootScope.layer << " scope layer " << &m_scopeLayer << " rects " << m_rootScope.rectList;
|
---|
267 |
|
---|
268 | for (auto& childScope : m_rootScope.children)
|
---|
269 | recursiveOutputToStream(multilineStream, childScope, 1);
|
---|
270 |
|
---|
271 | return multilineStream.release();
|
---|
272 | }
|
---|
273 |
|
---|
274 | LayerOverlapMap::LayerOverlapMap(const RenderLayer& rootLayer)
|
---|
275 | : m_geometryMap(UseTransforms)
|
---|
276 | , m_rootLayer(rootLayer)
|
---|
277 | {
|
---|
278 | // Begin assuming the root layer will be composited so that there is
|
---|
279 | // something on the stack. The root layer should also never get an
|
---|
280 | // popCompositingContainer call.
|
---|
281 | pushCompositingContainer(rootLayer);
|
---|
282 | }
|
---|
283 |
|
---|
284 | LayerOverlapMap::~LayerOverlapMap() = default;
|
---|
285 |
|
---|
286 | void LayerOverlapMap::add(const RenderLayer& layer, const LayoutRect& bounds, const Vector<LayerAndBounds>& enclosingClippingLayers)
|
---|
287 | {
|
---|
288 | // Layers do not contribute to overlap immediately--instead, they will
|
---|
289 | // contribute to overlap as soon as their composited ancestor has been
|
---|
290 | // recursively processed and popped off the stack.
|
---|
291 | ASSERT(m_overlapStack.size() >= 2);
|
---|
292 | auto& container = m_overlapStack[m_overlapStack.size() - 2];
|
---|
293 | container->add(layer, bounds, enclosingClippingLayers);
|
---|
294 |
|
---|
295 | LOG_WITH_STREAM(CompositingOverlap, stream << "layer " << &layer << " contributes to overlap in the scope of layer " << &container->scopeLayer() << ", added to map " << *this);
|
---|
296 |
|
---|
297 | m_isEmpty = false;
|
---|
298 | }
|
---|
299 |
|
---|
300 | bool LayerOverlapMap::overlapsLayers(const RenderLayer& layer, const LayoutRect& bounds, const Vector<LayerAndBounds>& enclosingClippingLayers) const
|
---|
301 | {
|
---|
302 | return m_overlapStack.last()->overlapsLayers(layer, bounds, enclosingClippingLayers);
|
---|
303 | }
|
---|
304 |
|
---|
305 | void LayerOverlapMap::pushCompositingContainer(const RenderLayer& layer)
|
---|
306 | {
|
---|
307 | m_overlapStack.append(makeUnique<OverlapMapContainer>(m_rootLayer, layer));
|
---|
308 | }
|
---|
309 |
|
---|
310 | void LayerOverlapMap::popCompositingContainer(const RenderLayer& layer)
|
---|
311 | {
|
---|
312 | ASSERT_UNUSED(layer, &m_overlapStack.last()->scopeLayer() == &layer);
|
---|
313 | m_overlapStack[m_overlapStack.size() - 2]->append(WTFMove(m_overlapStack.last()));
|
---|
314 | m_overlapStack.removeLast();
|
---|
315 | }
|
---|
316 |
|
---|
317 | static TextStream& operator<<(TextStream& ts, const OverlapMapContainer& container)
|
---|
318 | {
|
---|
319 | ts << container.dump(ts.indent());
|
---|
320 | return ts;
|
---|
321 | }
|
---|
322 |
|
---|
323 | TextStream& operator<<(TextStream& ts, const LayerOverlapMap& overlapMap)
|
---|
324 | {
|
---|
325 | TextStream multilineStream;
|
---|
326 |
|
---|
327 | TextStream::GroupScope scope(ts);
|
---|
328 | multilineStream << "LayerOverlapMap\n";
|
---|
329 | multilineStream.increaseIndent(2);
|
---|
330 |
|
---|
331 | bool needNewline = false;
|
---|
332 | for (auto& container : overlapMap.overlapStack()) {
|
---|
333 | if (needNewline)
|
---|
334 | multilineStream << "\n";
|
---|
335 | else
|
---|
336 | needNewline = true;
|
---|
337 | multilineStream << indent << *container;
|
---|
338 | }
|
---|
339 |
|
---|
340 | ts << multilineStream.release();
|
---|
341 | return ts;
|
---|
342 | }
|
---|
343 |
|
---|
344 | } // namespace WebCore
|
---|
345 |
|
---|