e228b4b7cc40f0a24f9be6ce6696323c3f004084
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / rendering / RenderLayerClipper.cpp
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 WebCore {
51
52 void RenderLayerClipper::updateClipRects(const ClipRectsContext& clipRectsContext)
53 {
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);
60
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;
65         ClipRects clipRects;
66         calculateClipRects(tempContext, clipRects);
67         ASSERT(clipRects == *m_clipRectsCache->getClipRects(clipRectsType, clipRectsContext.respectOverflowClip).get());
68 #endif
69         return; // We have the correct cached value.
70     }
71
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;
75     if (parentLayer)
76         parentLayer->clipper().updateClipRects(clipRectsContext);
77
78     ClipRects clipRects;
79     calculateClipRects(clipRectsContext, clipRects);
80
81     if (!m_clipRectsCache)
82         m_clipRectsCache = adoptPtr(new ClipRectsCache);
83
84     if (parentLayer && parentLayer->clipper().clipRects(clipRectsContext) && clipRects == *parentLayer->clipper().clipRects(clipRectsContext))
85         m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, parentLayer->clipper().clipRects(clipRectsContext));
86     else
87         m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, ClipRects::create(clipRects));
88
89 #ifndef NDEBUG
90     m_clipRectsCache->m_clipRectsRoot[clipRectsType] = clipRectsContext.rootLayer;
91     m_clipRectsCache->m_scrollbarRelevancy[clipRectsType] = clipRectsContext.overlayScrollbarSizeRelevancy;
92 #endif
93 }
94
95 void RenderLayerClipper::clearClipRectsIncludingDescendants(ClipRectsType typeToClear)
96 {
97     // FIXME: it's not clear how this layer not having clip rects guarantees that no descendants have any.
98     if (!m_clipRectsCache)
99         return;
100
101     clearClipRects(typeToClear);
102
103     for (RenderLayer* layer = m_renderer->layer()->firstChild(); layer; layer = layer->nextSibling())
104         layer->clipper().clearClipRectsIncludingDescendants(typeToClear);
105 }
106
107 void RenderLayerClipper::clearClipRects(ClipRectsType typeToClear)
108 {
109     if (typeToClear == AllClipRectTypes) {
110         m_clipRectsCache = nullptr;
111     } else {
112         ASSERT(typeToClear < NumCachedClipRectsTypes);
113         RefPtr<ClipRects> dummy;
114         m_clipRectsCache->setClipRects(typeToClear, RespectOverflowClip, dummy);
115         m_clipRectsCache->setClipRects(typeToClear, IgnoreOverflowClip, dummy);
116     }
117 }
118
119 LayoutRect RenderLayerClipper::childrenClipRect() const
120 {
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();
131 }
132
133 LayoutRect RenderLayerClipper::selfClipRect() const
134 {
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();
144 }
145
146 LayoutRect RenderLayerClipper::localClipRect() const
147 {
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);
155
156     LayoutRect clipRect = backgroundRect.rect();
157     if (clipRect == PaintInfo::infiniteRect())
158         return clipRect;
159
160     LayoutPoint clippingRootOffset;
161     m_renderer->layer()->convertToLayerCoords(clippingRootLayer, clippingRootOffset);
162     clipRect.moveBy(-clippingRootOffset);
163
164     return clipRect;
165 }
166
167 void RenderLayerClipper::calculateRects(const ClipRectsContext& clipRectsContext, const LayoutRect& paintDirtyRect, LayoutRect& layerBounds,
168     ClipRect& backgroundRect, ClipRect& foregroundRect, ClipRect& outlineRect, const LayoutPoint* offsetFromRoot) const
169 {
170     if (clipRectsContext.rootLayer != m_renderer->layer() && m_renderer->layer()->parent()) {
171         backgroundRect = backgroundClipRect(clipRectsContext);
172         backgroundRect.intersect(paintDirtyRect);
173     } else {
174         backgroundRect = paintDirtyRect;
175     }
176
177     foregroundRect = backgroundRect;
178     outlineRect = backgroundRect;
179
180     LayoutPoint offset;
181     if (offsetFromRoot)
182         offset = *offsetFromRoot;
183     else
184         m_renderer->layer()->convertToLayerCoords(clipRectsContext.rootLayer, offset);
185     layerBounds = LayoutRect(offset, m_renderer->layer()->size());
186
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);
194         }
195
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);
209         } else {
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);
215         }
216     }
217
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);
225     }
226 }
227
228 void RenderLayerClipper::calculateClipRects(const ClipRectsContext& clipRectsContext, ClipRects& clipRects) const
229 {
230     if (!m_renderer->layer()->parent()) {
231         // The root layer's clip rect is always infinite.
232         clipRects.reset(PaintInfo::infiniteRect());
233         return;
234     }
235
236     ClipRectsType clipRectsType = clipRectsContext.clipRectsType;
237     bool useCached = clipRectsType != TemporaryClipRects;
238
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;
242
243     // Ensure that our parent's clip has been calculated so that we can examine the values.
244     if (parentLayer) {
245         if (useCached && parentLayer->clipper().clipRects(clipRectsContext)) {
246             clipRects = *parentLayer->clipper().clipRects(clipRectsContext);
247         } else {
248             ClipRectsContext parentContext(clipRectsContext);
249             parentContext.overlayScrollbarSizeRelevancy = IgnoreOverlayScrollbarSize; // FIXME: why?
250             parentLayer->clipper().calculateClipRects(parentContext, clipRects);
251         }
252     } else {
253         clipRects.reset(PaintInfo::infiniteRect());
254     }
255
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());
266     }
267
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.
271
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.
275         LayoutPoint offset;
276         offset = roundedLayoutPoint(m_renderer->localToContainerPoint(FloatPoint(), clipRectsContext.rootLayer->renderer()));
277         RenderView* view = m_renderer->view();
278         ASSERT(view);
279         if (view && clipRects.fixed() && clipRectsContext.rootLayer->renderer() == view) {
280             offset -= view->frameView()->scrollOffsetForFixedPosition();
281         }
282
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()));
290         }
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()));
296         }
297     }
298 }
299
300 static inline ClipRect backgroundClipRectForPosition(const ClipRects& parentRects, EPosition position)
301 {
302     if (position == FixedPosition)
303         return parentRects.fixedClipRect();
304
305     if (position == AbsolutePosition)
306         return parentRects.posClipRect();
307
308     return parentRects.overflowClipRect();
309 }
310
311 ClipRect RenderLayerClipper::backgroundClipRect(const ClipRectsContext& clipRectsContext) const
312 {
313     ASSERT(m_renderer->layer()->parent());
314
315     ClipRects parentRects;
316
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);
323     } else {
324         parentClipRects(clipRectsContext, parentRects);
325     }
326
327     ClipRect backgroundClipRect = backgroundClipRectForPosition(parentRects, m_renderer->style()->position());
328     RenderView* view = m_renderer->view();
329     ASSERT(view);
330
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());
334
335     return backgroundClipRect;
336 }
337
338 void RenderLayerClipper::parentClipRects(const ClipRectsContext& clipRectsContext, ClipRects& clipRects) const
339 {
340     ASSERT(m_renderer->layer()->parent());
341
342     RenderLayerClipper& parentClipper = m_renderer->layer()->parent()->clipper();
343     if (clipRectsContext.clipRectsType == TemporaryClipRects) {
344         parentClipper.calculateClipRects(clipRectsContext, clipRects);
345         return;
346     }
347
348     parentClipper.updateClipRects(clipRectsContext);
349     clipRects = *parentClipper.clipRects(clipRectsContext);
350 }
351
352 RenderLayer* RenderLayerClipper::clippingRootForPainting() const
353 {
354     if (m_renderer->hasCompositedLayerMapping())
355         return const_cast<RenderLayer*>(m_renderer->layer());
356
357     const RenderLayer* current = m_renderer->layer();
358     while (current) {
359         if (current->isRootLayer())
360             return const_cast<RenderLayer*>(current);
361
362         current = current->compositingContainer();
363         ASSERT(current);
364         if (current->transform()
365             || (current->compositingState() == PaintsIntoOwnBacking)
366         )
367             return const_cast<RenderLayer*>(current);
368     }
369
370     ASSERT_NOT_REACHED();
371     return 0;
372 }
373
374 } // namespace WebCore