• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
3  *
4  * Portions are Copyright (C) 1998 Netscape Communications Corporation.
5  *
6  * Other contributors:
7  *   Robert O'Callahan <roc+@cs.cmu.edu>
8  *   David Baron <dbaron@fas.harvard.edu>
9  *   Christian Biesinger <cbiesinger@web.de>
10  *   Randall Jesup <rjesup@wgate.com>
11  *   Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
12  *   Josh Soref <timeless@mac.com>
13  *   Boris Zbarsky <bzbarsky@mit.edu>
14  *
15  * This library is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU Lesser General Public
17  * License as published by the Free Software Foundation; either
18  * version 2.1 of the License, or (at your option) any later version.
19  *
20  * This library is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23  * Lesser General Public License for more details.
24  *
25  * You should have received a copy of the GNU Lesser General Public
26  * License along with this library; if not, write to the Free Software
27  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
28  *
29  * Alternatively, the contents of this file may be used under the terms
30  * of either the Mozilla Public License Version 1.1, found at
31  * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
32  * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
33  * (the "GPL"), in which case the provisions of the MPL or the GPL are
34  * applicable instead of those above.  If you wish to allow use of your
35  * version of this file only under the terms of one of those two
36  * licenses (the MPL or the GPL) and not to allow others to use your
37  * version of this file under the LGPL, indicate your decision by
38  * deletingthe provisions above and replace them with the notice and
39  * other provisions required by the MPL or the GPL, as the case may be.
40  * If you do not delete the provisions above, a recipient may use your
41  * version of this file under any of the LGPL, the MPL or the GPL.
42  */
43 
44 #include "config.h"
45 #include "core/rendering/RenderLayerClipper.h"
46 
47 #include "core/rendering/RenderLayer.h"
48 #include "core/rendering/RenderView.h"
49 
50 namespace blink {
51 
adjustClipRectsForChildren(const RenderObject & renderer,ClipRects & clipRects)52 static void adjustClipRectsForChildren(const RenderObject& renderer, ClipRects& clipRects)
53 {
54     EPosition position = renderer.style()->position();
55     // A fixed object is essentially the root of its containing block hierarchy, so when
56     // we encounter such an object, we reset our clip rects to the fixedClipRect.
57     if (position == FixedPosition) {
58         clipRects.setPosClipRect(clipRects.fixedClipRect());
59         clipRects.setOverflowClipRect(clipRects.fixedClipRect());
60         clipRects.setFixed(true);
61     } else if (position == RelativePosition) {
62         clipRects.setPosClipRect(clipRects.overflowClipRect());
63     } else if (position == AbsolutePosition) {
64         clipRects.setOverflowClipRect(clipRects.posClipRect());
65     }
66 }
67 
applyClipRects(const ClipRectsContext & context,RenderObject & renderer,LayoutPoint offset,ClipRects & clipRects)68 static void applyClipRects(const ClipRectsContext& context, RenderObject& renderer, LayoutPoint offset, ClipRects& clipRects)
69 {
70     ASSERT(renderer.hasOverflowClip() || renderer.hasClip());
71 
72     RenderView* view = renderer.view();
73     ASSERT(view);
74     if (clipRects.fixed() && context.rootLayer->renderer() == view)
75         offset -= view->frameView()->scrollOffsetForFixedPosition();
76 
77     if (renderer.hasOverflowClip()) {
78         ClipRect newOverflowClip = toRenderBox(renderer).overflowClipRect(offset, context.scrollbarRelevancy);
79         newOverflowClip.setHasRadius(renderer.style()->hasBorderRadius());
80         clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect()));
81         if (renderer.isPositioned())
82             clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect()));
83     }
84 
85     if (renderer.hasClip()) {
86         LayoutRect newClip = toRenderBox(renderer).clipRect(offset);
87         clipRects.setPosClipRect(intersection(newClip, clipRects.posClipRect()));
88         clipRects.setOverflowClipRect(intersection(newClip, clipRects.overflowClipRect()));
89         clipRects.setFixedClipRect(intersection(newClip, clipRects.fixedClipRect()));
90     }
91 }
92 
RenderLayerClipper(RenderLayerModelObject & renderer)93 RenderLayerClipper::RenderLayerClipper(RenderLayerModelObject& renderer)
94     : m_renderer(renderer)
95 {
96 }
97 
clipRectsIfCached(const ClipRectsContext & context) const98 ClipRects* RenderLayerClipper::clipRectsIfCached(const ClipRectsContext& context) const
99 {
100     ASSERT(context.usesCache());
101     if (!m_cache)
102         return 0;
103     ClipRectsCache::Entry& entry = m_cache->get(context.cacheSlot);
104     // FIXME: We used to ASSERT that we always got a consistent root layer.
105     // We should add a test that has an inconsistent root. See
106     // http://crbug.com/366118 for an example.
107     if (context.rootLayer != entry.root)
108         return 0;
109     ASSERT(entry.scrollbarRelevancy == context.scrollbarRelevancy);
110 
111 #ifdef CHECK_CACHED_CLIP_RECTS
112     // This code is useful to check cached clip rects, but is too expensive to leave enabled in debug builds by default.
113     ClipRectsContext tempContext(context);
114     tempContext.cacheSlot = UncachedClipRects;
115     ClipRects clipRects;
116     calculateClipRects(tempContext, clipRects);
117     ASSERT(clipRects == *entry.clipRects);
118 #endif
119 
120     return entry.clipRects.get();
121 }
122 
storeClipRectsInCache(const ClipRectsContext & context,ClipRects * parentClipRects,const ClipRects & clipRects) const123 ClipRects* RenderLayerClipper::storeClipRectsInCache(const ClipRectsContext& context, ClipRects* parentClipRects, const ClipRects& clipRects) const
124 {
125     ClipRectsCache::Entry& entry = cache().get(context.cacheSlot);
126     entry.root = context.rootLayer;
127 #if ENABLE(ASSERT)
128     entry.scrollbarRelevancy = context.scrollbarRelevancy;
129 #endif
130 
131     if (parentClipRects) {
132         // If our clip rects match the clip rects of our parent, we share storage.
133         if (clipRects == *parentClipRects) {
134             entry.clipRects = parentClipRects;
135             return parentClipRects;
136         }
137     }
138 
139     entry.clipRects = ClipRects::create(clipRects);
140     return entry.clipRects.get();
141 }
142 
getClipRects(const ClipRectsContext & context) const143 ClipRects* RenderLayerClipper::getClipRects(const ClipRectsContext& context) const
144 {
145     if (ClipRects* result = clipRectsIfCached(context))
146         return result;
147 
148     // Note that it's important that we call getClipRects on our parent
149     // before we call calculateClipRects so that calculateClipRects will hit
150     // the cache.
151     ClipRects* parentClipRects = 0;
152     if (context.rootLayer != m_renderer.layer() && m_renderer.layer()->parent())
153         parentClipRects = m_renderer.layer()->parent()->clipper().getClipRects(context);
154 
155     ClipRects clipRects;
156     calculateClipRects(context, clipRects);
157     return storeClipRectsInCache(context, parentClipRects, clipRects);
158 }
159 
clearClipRectsIncludingDescendants()160 void RenderLayerClipper::clearClipRectsIncludingDescendants()
161 {
162     m_cache = nullptr;
163 
164     for (RenderLayer* layer = m_renderer.layer()->firstChild(); layer; layer = layer->nextSibling())
165         layer->clipper().clearClipRectsIncludingDescendants();
166 }
167 
clearClipRectsIncludingDescendants(ClipRectsCacheSlot cacheSlot)168 void RenderLayerClipper::clearClipRectsIncludingDescendants(ClipRectsCacheSlot cacheSlot)
169 {
170     if (m_cache)
171         m_cache->clear(cacheSlot);
172 
173     for (RenderLayer* layer = m_renderer.layer()->firstChild(); layer; layer = layer->nextSibling())
174         layer->clipper().clearClipRectsIncludingDescendants(cacheSlot);
175 }
176 
childrenClipRect() const177 LayoutRect RenderLayerClipper::childrenClipRect() const
178 {
179     // FIXME: border-radius not accounted for.
180     // FIXME: Regions not accounted for.
181     RenderLayer* clippingRootLayer = clippingRootForPainting();
182     LayoutRect layerBounds;
183     ClipRect backgroundRect, foregroundRect, outlineRect;
184     // Need to use uncached clip rects, because the value of 'dontClipToOverflow' may be different from the painting path (<rdar://problem/11844909>).
185     ClipRectsContext context(clippingRootLayer, UncachedClipRects);
186     calculateRects(context, m_renderer.view()->unscaledDocumentRect(), layerBounds, backgroundRect, foregroundRect, outlineRect);
187     return clippingRootLayer->renderer()->localToAbsoluteQuad(FloatQuad(foregroundRect.rect())).enclosingBoundingBox();
188 }
189 
localClipRect() const190 LayoutRect RenderLayerClipper::localClipRect() const
191 {
192     // FIXME: border-radius not accounted for.
193     RenderLayer* clippingRootLayer = clippingRootForPainting();
194     LayoutRect layerBounds;
195     ClipRect backgroundRect, foregroundRect, outlineRect;
196     ClipRectsContext context(clippingRootLayer, PaintingClipRects);
197     calculateRects(context, PaintInfo::infiniteRect(), layerBounds, backgroundRect, foregroundRect, outlineRect);
198 
199     LayoutRect clipRect = backgroundRect.rect();
200     if (clipRect == PaintInfo::infiniteRect())
201         return clipRect;
202 
203     LayoutPoint clippingRootOffset;
204     m_renderer.layer()->convertToLayerCoords(clippingRootLayer, clippingRootOffset);
205     clipRect.moveBy(-clippingRootOffset);
206 
207     return clipRect;
208 }
209 
calculateRects(const ClipRectsContext & context,const LayoutRect & paintDirtyRect,LayoutRect & layerBounds,ClipRect & backgroundRect,ClipRect & foregroundRect,ClipRect & outlineRect,const LayoutPoint * offsetFromRoot) const210 void RenderLayerClipper::calculateRects(const ClipRectsContext& context, const LayoutRect& paintDirtyRect, LayoutRect& layerBounds,
211     ClipRect& backgroundRect, ClipRect& foregroundRect, ClipRect& outlineRect, const LayoutPoint* offsetFromRoot) const
212 {
213     bool isClippingRoot = m_renderer.layer() == context.rootLayer;
214 
215     if (!isClippingRoot && m_renderer.layer()->parent()) {
216         backgroundRect = backgroundClipRect(context);
217         backgroundRect.move(roundedIntSize(context.subPixelAccumulation));
218         backgroundRect.intersect(paintDirtyRect);
219     } else {
220         backgroundRect = paintDirtyRect;
221     }
222 
223     foregroundRect = backgroundRect;
224     outlineRect = backgroundRect;
225 
226     LayoutPoint offset;
227     if (offsetFromRoot)
228         offset = *offsetFromRoot;
229     else
230         m_renderer.layer()->convertToLayerCoords(context.rootLayer, offset);
231     layerBounds = LayoutRect(offset, m_renderer.layer()->size());
232 
233     // Update the clip rects that will be passed to child layers.
234     if (m_renderer.hasOverflowClip()) {
235         // This layer establishes a clip of some kind.
236         if (!isClippingRoot || context.respectOverflowClip == RespectOverflowClip) {
237             foregroundRect.intersect(toRenderBox(m_renderer).overflowClipRect(offset, context.scrollbarRelevancy));
238             if (m_renderer.style()->hasBorderRadius())
239                 foregroundRect.setHasRadius(true);
240         }
241 
242         // If we establish an overflow clip at all, then go ahead and make sure our background
243         // rect is intersected with our layer's bounds including our visual overflow,
244         // since any visual overflow like box-shadow or border-outset is not clipped by overflow:auto/hidden.
245         if (toRenderBox(m_renderer).hasVisualOverflow()) {
246             // FIXME: Perhaps we should be propagating the borderbox as the clip rect for children, even though
247             //        we may need to inflate our clip specifically for shadows or outsets.
248             // FIXME: Does not do the right thing with CSS regions yet, since we don't yet factor in the
249             // individual region boxes as overflow.
250             LayoutRect layerBoundsWithVisualOverflow = toRenderBox(m_renderer).visualOverflowRect();
251             toRenderBox(m_renderer).flipForWritingMode(layerBoundsWithVisualOverflow); // Layers are in physical coordinates, so the overflow has to be flipped.
252             layerBoundsWithVisualOverflow.moveBy(offset);
253             if (!isClippingRoot || context.respectOverflowClip == RespectOverflowClip)
254                 backgroundRect.intersect(layerBoundsWithVisualOverflow);
255         } else {
256             LayoutRect bounds = toRenderBox(m_renderer).borderBoxRect();
257             bounds.moveBy(offset);
258             if (!isClippingRoot || context.respectOverflowClip == RespectOverflowClip)
259                 backgroundRect.intersect(bounds);
260         }
261     }
262 
263     // CSS clip (different than clipping due to overflow) can clip to any box, even if it falls outside of the border box.
264     if (m_renderer.hasClip()) {
265         // Clip applies to *us* as well, so go ahead and update the damageRect.
266         LayoutRect newPosClip = toRenderBox(m_renderer).clipRect(offset);
267         backgroundRect.intersect(newPosClip);
268         foregroundRect.intersect(newPosClip);
269         outlineRect.intersect(newPosClip);
270     }
271 }
272 
calculateClipRects(const ClipRectsContext & context,ClipRects & clipRects) const273 void RenderLayerClipper::calculateClipRects(const ClipRectsContext& context, ClipRects& clipRects) const
274 {
275     if (!m_renderer.layer()->parent()) {
276         // The root layer's clip rect is always infinite.
277         clipRects.reset(PaintInfo::infiniteRect());
278         return;
279     }
280 
281     bool isClippingRoot = m_renderer.layer() == context.rootLayer;
282 
283     // For transformed layers, the root layer was shifted to be us, so there is no need to
284     // examine the parent. We want to cache clip rects with us as the root.
285     RenderLayer* parentLayer = !isClippingRoot ? m_renderer.layer()->parent() : 0;
286 
287     // Ensure that our parent's clip has been calculated so that we can examine the values.
288     if (parentLayer) {
289         // FIXME: Why don't we just call getClipRects here?
290         if (context.usesCache() && parentLayer->clipper().cachedClipRects(context)) {
291             clipRects = *parentLayer->clipper().cachedClipRects(context);
292         } else {
293             parentLayer->clipper().calculateClipRects(context, clipRects);
294         }
295     } else {
296         clipRects.reset(PaintInfo::infiniteRect());
297     }
298 
299     adjustClipRectsForChildren(m_renderer, clipRects);
300 
301     // FIXME: This logic looks wrong. We'll apply overflow clip rects even if we were told to IgnoreOverflowClip if m_renderer.hasClip().
302     if ((m_renderer.hasOverflowClip() && (context.respectOverflowClip == RespectOverflowClip || !isClippingRoot)) || m_renderer.hasClip()) {
303         // This offset cannot use convertToLayerCoords, because sometimes our rootLayer may be across
304         // some transformed layer boundary, for example, in the RenderLayerCompositor overlapMap, where
305         // clipRects are needed in view space.
306         applyClipRects(context, m_renderer, roundedLayoutPoint(m_renderer.localToContainerPoint(FloatPoint(), context.rootLayer->renderer())), clipRects);
307     }
308 }
309 
backgroundClipRectForPosition(const ClipRects & parentRects,EPosition position)310 static ClipRect backgroundClipRectForPosition(const ClipRects& parentRects, EPosition position)
311 {
312     if (position == FixedPosition)
313         return parentRects.fixedClipRect();
314 
315     if (position == AbsolutePosition)
316         return parentRects.posClipRect();
317 
318     return parentRects.overflowClipRect();
319 }
320 
backgroundClipRect(const ClipRectsContext & context) const321 ClipRect RenderLayerClipper::backgroundClipRect(const ClipRectsContext& context) const
322 {
323     ASSERT(m_renderer.layer()->parent());
324     ASSERT(m_renderer.view());
325 
326     ClipRects parentClipRects;
327     if (m_renderer.layer() == context.rootLayer)
328         parentClipRects.reset(PaintInfo::infiniteRect());
329     else
330         m_renderer.layer()->parent()->clipper().getOrCalculateClipRects(context, parentClipRects);
331 
332     ClipRect result = backgroundClipRectForPosition(parentClipRects, m_renderer.style()->position());
333 
334     // Note: infinite clipRects should not be scrolled here, otherwise they will accidentally no longer be considered infinite.
335     if (parentClipRects.fixed() && context.rootLayer->renderer() == m_renderer.view() && result != PaintInfo::infiniteRect())
336         result.move(m_renderer.view()->frameView()->scrollOffsetForFixedPosition());
337 
338     return result;
339 }
340 
getOrCalculateClipRects(const ClipRectsContext & context,ClipRects & clipRects) const341 void RenderLayerClipper::getOrCalculateClipRects(const ClipRectsContext& context, ClipRects& clipRects) const
342 {
343     if (context.usesCache())
344         clipRects = *getClipRects(context);
345     else
346         calculateClipRects(context, clipRects);
347 }
348 
clippingRootForPainting() const349 RenderLayer* RenderLayerClipper::clippingRootForPainting() const
350 {
351     const RenderLayer* current = m_renderer.layer();
352     // FIXME: getting rid of current->hasCompositedLayerMapping() here breaks the
353     // compositing/backing/no-backing-for-clip.html layout test, because there is a
354     // "composited but paints into ancestor" layer involved. However, it doesn't make sense that
355     // that check would be appropriate here but not inside the while loop below.
356     if (current->isPaintInvalidationContainer() || current->hasCompositedLayerMapping())
357         return const_cast<RenderLayer*>(current);
358 
359     while (current) {
360         if (current->isRootLayer())
361             return const_cast<RenderLayer*>(current);
362 
363         current = current->compositingContainer();
364         ASSERT(current);
365         if (current->transform() || current->isPaintInvalidationContainer())
366             return const_cast<RenderLayer*>(current);
367     }
368 
369     ASSERT_NOT_REACHED();
370     return 0;
371 }
372 
373 } // namespace blink
374