2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
4 * Portions are Copyright (C) 1998 Netscape Communications Corporation.
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>
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.
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.
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
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.
45 #include "core/rendering/RenderLayerClipper.h"
47 #include "core/rendering/RenderLayer.h"
48 #include "core/rendering/RenderView.h"
52 void RenderLayerClipper::updateClipRects(const ClipRectsContext& clipRectsContext)
54 ClipRectsType clipRectsType = clipRectsContext.clipRectsType;
55 ASSERT(clipRectsType < NumCachedClipRectsTypes);
56 if (m_clipRectsCache && m_clipRectsCache->getClipRects(clipRectsType, clipRectsContext.respectOverflowClip)) {
57 // FIXME: these asserts trigger for squashing. Need to update this code to support squashing as appropriate.
58 ASSERT(clipRectsContext.rootLayer == m_clipRectsCache->m_clipRectsRoot[clipRectsType]);
59 ASSERT(m_clipRectsCache->m_scrollbarRelevancy[clipRectsType] == clipRectsContext.overlayScrollbarSizeRelevancy);
61 #ifdef CHECK_CACHED_CLIP_RECTS
62 // This code is useful to check cached clip rects, but is too expensive to leave enabled in debug builds by default.
63 ClipRectsContext tempContext(clipRectsContext);
64 tempContext.clipRectsType = TemporaryClipRects;
66 calculateClipRects(tempContext, clipRects);
67 ASSERT(clipRects == *m_clipRectsCache->getClipRects(clipRectsType, clipRectsContext.respectOverflowClip).get());
69 return; // We have the correct cached value.
72 // For transformed layers, the root layer was shifted to be us, so there is no need to
73 // examine the parent. We want to cache clip rects with us as the root.
74 RenderLayer* parentLayer = clipRectsContext.rootLayer != m_renderer->layer() ? m_renderer->layer()->parent() : 0;
76 parentLayer->clipper().updateClipRects(clipRectsContext);
79 calculateClipRects(clipRectsContext, clipRects);
81 if (!m_clipRectsCache)
82 m_clipRectsCache = adoptPtr(new ClipRectsCache);
84 if (parentLayer && parentLayer->clipper().clipRects(clipRectsContext) && clipRects == *parentLayer->clipper().clipRects(clipRectsContext))
85 m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, parentLayer->clipper().clipRects(clipRectsContext));
87 m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, ClipRects::create(clipRects));
90 m_clipRectsCache->m_clipRectsRoot[clipRectsType] = clipRectsContext.rootLayer;
91 m_clipRectsCache->m_scrollbarRelevancy[clipRectsType] = clipRectsContext.overlayScrollbarSizeRelevancy;
95 void RenderLayerClipper::clearClipRectsIncludingDescendants(ClipRectsType typeToClear)
97 // FIXME: it's not clear how this layer not having clip rects guarantees that no descendants have any.
98 if (!m_clipRectsCache)
101 clearClipRects(typeToClear);
103 for (RenderLayer* layer = m_renderer->layer()->firstChild(); layer; layer = layer->nextSibling())
104 layer->clipper().clearClipRectsIncludingDescendants(typeToClear);
107 void RenderLayerClipper::clearClipRects(ClipRectsType typeToClear)
109 if (typeToClear == AllClipRectTypes) {
110 m_clipRectsCache = nullptr;
112 ASSERT(typeToClear < NumCachedClipRectsTypes);
113 RefPtr<ClipRects> dummy;
114 m_clipRectsCache->setClipRects(typeToClear, RespectOverflowClip, dummy);
115 m_clipRectsCache->setClipRects(typeToClear, IgnoreOverflowClip, dummy);
119 LayoutRect RenderLayerClipper::childrenClipRect() const
121 // FIXME: border-radius not accounted for.
122 // FIXME: Regions not accounted for.
123 RenderView* renderView = m_renderer->view();
124 RenderLayer* clippingRootLayer = clippingRootForPainting();
125 LayoutRect layerBounds;
126 ClipRect backgroundRect, foregroundRect, outlineRect;
127 ClipRectsContext clipRectsContext(clippingRootLayer, 0, TemporaryClipRects);
128 // Need to use temporary clip rects, because the value of 'dontClipToOverflow' may be different from the painting path (<rdar://problem/11844909>).
129 calculateRects(clipRectsContext, renderView->unscaledDocumentRect(), layerBounds, backgroundRect, foregroundRect, outlineRect);
130 return clippingRootLayer->renderer()->localToAbsoluteQuad(FloatQuad(foregroundRect.rect())).enclosingBoundingBox();
133 LayoutRect RenderLayerClipper::selfClipRect() const
135 // FIXME: border-radius not accounted for.
136 // FIXME: Regions not accounted for.
137 RenderView* renderView = m_renderer->view();
138 RenderLayer* clippingRootLayer = clippingRootForPainting();
139 LayoutRect layerBounds;
140 ClipRect backgroundRect, foregroundRect, outlineRect;
141 ClipRectsContext clipRectsContext(clippingRootLayer, 0, PaintingClipRects);
142 calculateRects(clipRectsContext, renderView->documentRect(), layerBounds, backgroundRect, foregroundRect, outlineRect);
143 return clippingRootLayer->renderer()->localToAbsoluteQuad(FloatQuad(backgroundRect.rect())).enclosingBoundingBox();
146 LayoutRect RenderLayerClipper::localClipRect() const
148 // FIXME: border-radius not accounted for.
149 // FIXME: Regions not accounted for.
150 RenderLayer* clippingRootLayer = clippingRootForPainting();
151 LayoutRect layerBounds;
152 ClipRect backgroundRect, foregroundRect, outlineRect;
153 ClipRectsContext clipRectsContext(clippingRootLayer, 0, PaintingClipRects);
154 calculateRects(clipRectsContext, PaintInfo::infiniteRect(), layerBounds, backgroundRect, foregroundRect, outlineRect);
156 LayoutRect clipRect = backgroundRect.rect();
157 if (clipRect == PaintInfo::infiniteRect())
160 LayoutPoint clippingRootOffset;
161 m_renderer->layer()->convertToLayerCoords(clippingRootLayer, clippingRootOffset);
162 clipRect.moveBy(-clippingRootOffset);
167 void RenderLayerClipper::calculateRects(const ClipRectsContext& clipRectsContext, const LayoutRect& paintDirtyRect, LayoutRect& layerBounds,
168 ClipRect& backgroundRect, ClipRect& foregroundRect, ClipRect& outlineRect, const LayoutPoint* offsetFromRoot) const
170 if (clipRectsContext.rootLayer != m_renderer->layer() && m_renderer->layer()->parent()) {
171 backgroundRect = backgroundClipRect(clipRectsContext);
172 backgroundRect.intersect(paintDirtyRect);
174 backgroundRect = paintDirtyRect;
177 foregroundRect = backgroundRect;
178 outlineRect = backgroundRect;
182 offset = *offsetFromRoot;
184 m_renderer->layer()->convertToLayerCoords(clipRectsContext.rootLayer, offset);
185 layerBounds = LayoutRect(offset, m_renderer->layer()->size());
187 // Update the clip rects that will be passed to child layers.
188 if (m_renderer->hasOverflowClip()) {
189 // This layer establishes a clip of some kind.
190 if (m_renderer->layer() != clipRectsContext.rootLayer || clipRectsContext.respectOverflowClip == RespectOverflowClip) {
191 foregroundRect.intersect(toRenderBox(m_renderer)->overflowClipRect(offset, clipRectsContext.region, clipRectsContext.overlayScrollbarSizeRelevancy));
192 if (m_renderer->style()->hasBorderRadius())
193 foregroundRect.setHasRadius(true);
196 // If we establish an overflow clip at all, then go ahead and make sure our background
197 // rect is intersected with our layer's bounds including our visual overflow,
198 // since any visual overflow like box-shadow or border-outset is not clipped by overflow:auto/hidden.
199 if (toRenderBox(m_renderer)->hasVisualOverflow()) {
200 // FIXME: Perhaps we should be propagating the borderbox as the clip rect for children, even though
201 // we may need to inflate our clip specifically for shadows or outsets.
202 // FIXME: Does not do the right thing with CSS regions yet, since we don't yet factor in the
203 // individual region boxes as overflow.
204 LayoutRect layerBoundsWithVisualOverflow = toRenderBox(m_renderer)->visualOverflowRect();
205 toRenderBox(m_renderer)->flipForWritingMode(layerBoundsWithVisualOverflow); // Layers are in physical coordinates, so the overflow has to be flipped.
206 layerBoundsWithVisualOverflow.moveBy(offset);
207 if (m_renderer->layer() != clipRectsContext.rootLayer || clipRectsContext.respectOverflowClip == RespectOverflowClip)
208 backgroundRect.intersect(layerBoundsWithVisualOverflow);
210 // Shift the bounds to be for our region only.
211 LayoutRect bounds = toRenderBox(m_renderer)->borderBoxRectInRegion(clipRectsContext.region);
212 bounds.moveBy(offset);
213 if (m_renderer->layer() != clipRectsContext.rootLayer || clipRectsContext.respectOverflowClip == RespectOverflowClip)
214 backgroundRect.intersect(bounds);
218 // CSS clip (different than clipping due to overflow) can clip to any box, even if it falls outside of the border box.
219 if (m_renderer->hasClip()) {
220 // Clip applies to *us* as well, so go ahead and update the damageRect.
221 LayoutRect newPosClip = toRenderBox(m_renderer)->clipRect(offset, clipRectsContext.region);
222 backgroundRect.intersect(newPosClip);
223 foregroundRect.intersect(newPosClip);
224 outlineRect.intersect(newPosClip);
228 void RenderLayerClipper::calculateClipRects(const ClipRectsContext& clipRectsContext, ClipRects& clipRects) const
230 if (!m_renderer->layer()->parent()) {
231 // The root layer's clip rect is always infinite.
232 clipRects.reset(PaintInfo::infiniteRect());
236 ClipRectsType clipRectsType = clipRectsContext.clipRectsType;
237 bool useCached = clipRectsType != TemporaryClipRects;
239 // For transformed layers, the root layer was shifted to be us, so there is no need to
240 // examine the parent. We want to cache clip rects with us as the root.
241 RenderLayer* parentLayer = clipRectsContext.rootLayer != m_renderer->layer() ? m_renderer->layer()->parent() : 0;
243 // Ensure that our parent's clip has been calculated so that we can examine the values.
245 if (useCached && parentLayer->clipper().clipRects(clipRectsContext)) {
246 clipRects = *parentLayer->clipper().clipRects(clipRectsContext);
248 ClipRectsContext parentContext(clipRectsContext);
249 parentContext.overlayScrollbarSizeRelevancy = IgnoreOverlayScrollbarSize; // FIXME: why?
250 parentLayer->clipper().calculateClipRects(parentContext, clipRects);
253 clipRects.reset(PaintInfo::infiniteRect());
256 // A fixed object is essentially the root of its containing block hierarchy, so when
257 // we encounter such an object, we reset our clip rects to the fixedClipRect.
258 if (m_renderer->style()->position() == FixedPosition) {
259 clipRects.setPosClipRect(clipRects.fixedClipRect());
260 clipRects.setOverflowClipRect(clipRects.fixedClipRect());
261 clipRects.setFixed(true);
262 } else if (m_renderer->style()->hasInFlowPosition()) {
263 clipRects.setPosClipRect(clipRects.overflowClipRect());
264 } else if (m_renderer->style()->position() == AbsolutePosition) {
265 clipRects.setOverflowClipRect(clipRects.posClipRect());
268 // Update the clip rects that will be passed to child layers.
269 if ((m_renderer->hasOverflowClip() && (clipRectsContext.respectOverflowClip == RespectOverflowClip || m_renderer->layer() != clipRectsContext.rootLayer)) || m_renderer->hasClip()) {
270 // This layer establishes a clip of some kind.
272 // This offset cannot use convertToLayerCoords, because sometimes our rootLayer may be across
273 // some transformed layer boundary, for example, in the RenderLayerCompositor overlapMap, where
274 // clipRects are needed in view space.
276 offset = roundedLayoutPoint(m_renderer->localToContainerPoint(FloatPoint(), clipRectsContext.rootLayer->renderer()));
277 RenderView* view = m_renderer->view();
279 if (view && clipRects.fixed() && clipRectsContext.rootLayer->renderer() == view) {
280 offset -= view->frameView()->scrollOffsetForFixedPosition();
283 if (m_renderer->hasOverflowClip()) {
284 ClipRect newOverflowClip = toRenderBox(m_renderer)->overflowClipRect(offset, clipRectsContext.region, clipRectsContext.overlayScrollbarSizeRelevancy);
285 if (m_renderer->style()->hasBorderRadius())
286 newOverflowClip.setHasRadius(true);
287 clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect()));
288 if (m_renderer->isPositioned())
289 clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect()));
291 if (m_renderer->hasClip()) {
292 LayoutRect newPosClip = toRenderBox(m_renderer)->clipRect(offset, clipRectsContext.region);
293 clipRects.setPosClipRect(intersection(newPosClip, clipRects.posClipRect()));
294 clipRects.setOverflowClipRect(intersection(newPosClip, clipRects.overflowClipRect()));
295 clipRects.setFixedClipRect(intersection(newPosClip, clipRects.fixedClipRect()));
300 static inline ClipRect backgroundClipRectForPosition(const ClipRects& parentRects, EPosition position)
302 if (position == FixedPosition)
303 return parentRects.fixedClipRect();
305 if (position == AbsolutePosition)
306 return parentRects.posClipRect();
308 return parentRects.overflowClipRect();
311 ClipRect RenderLayerClipper::backgroundClipRect(const ClipRectsContext& clipRectsContext) const
313 ASSERT(m_renderer->layer()->parent());
315 ClipRects parentRects;
317 // If we cross into a different pagination context, then we can't rely on the cache.
318 // Just switch over to using TemporaryClipRects.
319 if (clipRectsContext.clipRectsType != TemporaryClipRects && m_renderer->layer()->parent()->enclosingPaginationLayer() != m_renderer->layer()->enclosingPaginationLayer()) {
320 ClipRectsContext tempContext(clipRectsContext);
321 tempContext.clipRectsType = TemporaryClipRects;
322 parentClipRects(tempContext, parentRects);
324 parentClipRects(clipRectsContext, parentRects);
327 ClipRect backgroundClipRect = backgroundClipRectForPosition(parentRects, m_renderer->style()->position());
328 RenderView* view = m_renderer->view();
331 // Note: infinite clipRects should not be scrolled here, otherwise they will accidentally no longer be considered infinite.
332 if (parentRects.fixed() && clipRectsContext.rootLayer->renderer() == view && backgroundClipRect != PaintInfo::infiniteRect())
333 backgroundClipRect.move(view->frameView()->scrollOffsetForFixedPosition());
335 return backgroundClipRect;
338 void RenderLayerClipper::parentClipRects(const ClipRectsContext& clipRectsContext, ClipRects& clipRects) const
340 ASSERT(m_renderer->layer()->parent());
342 RenderLayerClipper& parentClipper = m_renderer->layer()->parent()->clipper();
343 if (clipRectsContext.clipRectsType == TemporaryClipRects) {
344 parentClipper.calculateClipRects(clipRectsContext, clipRects);
348 parentClipper.updateClipRects(clipRectsContext);
349 clipRects = *parentClipper.clipRects(clipRectsContext);
352 RenderLayer* RenderLayerClipper::clippingRootForPainting() const
354 if (m_renderer->hasCompositedLayerMapping())
355 return const_cast<RenderLayer*>(m_renderer->layer());
357 const RenderLayer* current = m_renderer->layer();
359 if (current->isRootLayer())
360 return const_cast<RenderLayer*>(current);
362 current = current->compositingContainer();
364 if (current->transform()
365 || (current->compositingState() == PaintsIntoOwnBacking)
367 return const_cast<RenderLayer*>(current);
370 ASSERT_NOT_REACHED();
374 } // namespace WebCore