OLD | NEW |
| (Empty) |
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 US
A | |
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/frame/Settings.h" | |
48 #include "core/rendering/RenderLayer.h" | |
49 #include "core/rendering/RenderView.h" | |
50 | |
51 namespace blink { | |
52 | |
53 static void adjustClipRectsForChildren(const RenderObject& renderer, ClipRects&
clipRects) | |
54 { | |
55 EPosition position = renderer.style()->position(); | |
56 // A fixed object is essentially the root of its containing block hierarchy,
so when | |
57 // we encounter such an object, we reset our clip rects to the fixedClipRect
. | |
58 if (position == FixedPosition) { | |
59 clipRects.setPosClipRect(clipRects.fixedClipRect()); | |
60 clipRects.setOverflowClipRect(clipRects.fixedClipRect()); | |
61 clipRects.setFixed(true); | |
62 } else if (position == RelativePosition) { | |
63 clipRects.setPosClipRect(clipRects.overflowClipRect()); | |
64 } else if (position == AbsolutePosition) { | |
65 clipRects.setOverflowClipRect(clipRects.posClipRect()); | |
66 } | |
67 } | |
68 | |
69 static void applyClipRects(const ClipRectsContext& context, RenderObject& render
er, LayoutPoint offset, ClipRects& clipRects) | |
70 { | |
71 ASSERT(renderer.hasOverflowClip() || renderer.hasClip()); | |
72 | |
73 RenderView* view = renderer.view(); | |
74 ASSERT(view); | |
75 if (clipRects.fixed() && context.rootLayer->renderer() == view) | |
76 offset -= view->frameView()->scrollOffsetForViewportConstrainedObjects()
; | |
77 | |
78 if (renderer.hasOverflowClip()) { | |
79 ClipRect newOverflowClip = toRenderBox(renderer).overflowClipRect(offset
, context.scrollbarRelevancy); | |
80 newOverflowClip.setHasRadius(renderer.style()->hasBorderRadius()); | |
81 clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.ov
erflowClipRect())); | |
82 if (renderer.isPositioned()) | |
83 clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.pos
ClipRect())); | |
84 } | |
85 | |
86 if (renderer.hasClip()) { | |
87 LayoutRect newClip = toRenderBox(renderer).clipRect(offset); | |
88 clipRects.setPosClipRect(intersection(newClip, clipRects.posClipRect()))
; | |
89 clipRects.setOverflowClipRect(intersection(newClip, clipRects.overflowCl
ipRect())); | |
90 clipRects.setFixedClipRect(intersection(newClip, clipRects.fixedClipRect
())); | |
91 } | |
92 } | |
93 | |
94 RenderLayerClipper::RenderLayerClipper(RenderLayerModelObject& renderer) | |
95 : m_renderer(renderer) | |
96 { | |
97 } | |
98 | |
99 ClipRects* RenderLayerClipper::clipRectsIfCached(const ClipRectsContext& context
) const | |
100 { | |
101 ASSERT(context.usesCache()); | |
102 if (!m_cache) | |
103 return 0; | |
104 ClipRectsCache::Entry& entry = m_cache->get(context.cacheSlot); | |
105 // FIXME: We used to ASSERT that we always got a consistent root layer. | |
106 // We should add a test that has an inconsistent root. See | |
107 // http://crbug.com/366118 for an example. | |
108 if (context.rootLayer != entry.root) | |
109 return 0; | |
110 ASSERT(entry.scrollbarRelevancy == context.scrollbarRelevancy); | |
111 | |
112 #ifdef CHECK_CACHED_CLIP_RECTS | |
113 // This code is useful to check cached clip rects, but is too expensive to l
eave enabled in debug builds by default. | |
114 ClipRectsContext tempContext(context); | |
115 tempContext.cacheSlot = UncachedClipRects; | |
116 RefPtr<ClipRects> clipRects = ClipRects::create(); | |
117 calculateClipRects(tempContext, *clipRects); | |
118 ASSERT(clipRects == *entry.clipRects); | |
119 #endif | |
120 | |
121 return entry.clipRects.get(); | |
122 } | |
123 | |
124 ClipRects* RenderLayerClipper::storeClipRectsInCache(const ClipRectsContext& con
text, ClipRects* parentClipRects, const ClipRects& clipRects) const | |
125 { | |
126 ClipRectsCache::Entry& entry = cache().get(context.cacheSlot); | |
127 entry.root = context.rootLayer; | |
128 #if ENABLE(ASSERT) | |
129 entry.scrollbarRelevancy = context.scrollbarRelevancy; | |
130 #endif | |
131 | |
132 if (parentClipRects) { | |
133 // If our clip rects match the clip rects of our parent, we share storag
e. | |
134 if (clipRects == *parentClipRects) { | |
135 entry.clipRects = parentClipRects; | |
136 return parentClipRects; | |
137 } | |
138 } | |
139 | |
140 entry.clipRects = ClipRects::create(clipRects); | |
141 return entry.clipRects.get(); | |
142 } | |
143 | |
144 ClipRects* RenderLayerClipper::getClipRects(const ClipRectsContext& context) con
st | |
145 { | |
146 if (ClipRects* result = clipRectsIfCached(context)) | |
147 return result; | |
148 | |
149 // Note that it's important that we call getClipRects on our parent | |
150 // before we call calculateClipRects so that calculateClipRects will hit | |
151 // the cache. | |
152 ClipRects* parentClipRects = 0; | |
153 if (context.rootLayer != m_renderer.layer() && m_renderer.layer()->parent()) | |
154 parentClipRects = m_renderer.layer()->parent()->clipper().getClipRects(c
ontext); | |
155 | |
156 RefPtr<ClipRects> clipRects = ClipRects::create(); | |
157 calculateClipRects(context, *clipRects); | |
158 return storeClipRectsInCache(context, parentClipRects, *clipRects); | |
159 } | |
160 | |
161 void RenderLayerClipper::clearClipRectsIncludingDescendants() | |
162 { | |
163 m_cache = nullptr; | |
164 | |
165 for (RenderLayer* layer = m_renderer.layer()->firstChild(); layer; layer = l
ayer->nextSibling()) | |
166 layer->clipper().clearClipRectsIncludingDescendants(); | |
167 } | |
168 | |
169 void RenderLayerClipper::clearClipRectsIncludingDescendants(ClipRectsCacheSlot c
acheSlot) | |
170 { | |
171 if (m_cache) | |
172 m_cache->clear(cacheSlot); | |
173 | |
174 for (RenderLayer* layer = m_renderer.layer()->firstChild(); layer; layer = l
ayer->nextSibling()) | |
175 layer->clipper().clearClipRectsIncludingDescendants(cacheSlot); | |
176 } | |
177 | |
178 LayoutRect RenderLayerClipper::childrenClipRect() const | |
179 { | |
180 // FIXME: border-radius not accounted for. | |
181 // FIXME: Regions not accounted for. | |
182 RenderLayer* clippingRootLayer = clippingRootForPainting(); | |
183 LayoutRect layerBounds; | |
184 ClipRect backgroundRect, foregroundRect, outlineRect; | |
185 // Need to use uncached clip rects, because the value of 'dontClipToOverflow
' may be different from the painting path (<rdar://problem/11844909>). | |
186 ClipRectsContext context(clippingRootLayer, UncachedClipRects); | |
187 calculateRects(context, m_renderer.view()->unscaledDocumentRect(), layerBoun
ds, backgroundRect, foregroundRect, outlineRect); | |
188 return clippingRootLayer->renderer()->localToAbsoluteQuad(FloatQuad(foregrou
ndRect.rect())).enclosingBoundingBox(); | |
189 } | |
190 | |
191 LayoutRect RenderLayerClipper::localClipRect() const | |
192 { | |
193 // FIXME: border-radius not accounted for. | |
194 RenderLayer* clippingRootLayer = clippingRootForPainting(); | |
195 LayoutRect layerBounds; | |
196 ClipRect backgroundRect, foregroundRect, outlineRect; | |
197 ClipRectsContext context(clippingRootLayer, PaintingClipRects); | |
198 calculateRects(context, LayoutRect::infiniteIntRect(), layerBounds, backgrou
ndRect, foregroundRect, outlineRect); | |
199 | |
200 LayoutRect clipRect = backgroundRect.rect(); | |
201 if (clipRect == LayoutRect::infiniteIntRect()) | |
202 return clipRect; | |
203 | |
204 LayoutPoint clippingRootOffset; | |
205 m_renderer.layer()->convertToLayerCoords(clippingRootLayer, clippingRootOffs
et); | |
206 clipRect.moveBy(-clippingRootOffset); | |
207 | |
208 return clipRect; | |
209 } | |
210 | |
211 void RenderLayerClipper::calculateRects(const ClipRectsContext& context, const L
ayoutRect& paintDirtyRect, LayoutRect& layerBounds, | |
212 ClipRect& backgroundRect, ClipRect& foregroundRect, ClipRect& outlineRect, c
onst LayoutPoint* offsetFromRoot) const | |
213 { | |
214 bool isClippingRoot = m_renderer.layer() == context.rootLayer; | |
215 | |
216 if (!isClippingRoot && m_renderer.layer()->parent()) { | |
217 backgroundRect = backgroundClipRect(context); | |
218 backgroundRect.move(roundedIntSize(context.subPixelAccumulation)); | |
219 backgroundRect.intersect(paintDirtyRect); | |
220 } else { | |
221 backgroundRect = paintDirtyRect; | |
222 } | |
223 | |
224 foregroundRect = backgroundRect; | |
225 outlineRect = backgroundRect; | |
226 | |
227 LayoutPoint offset; | |
228 if (offsetFromRoot) | |
229 offset = *offsetFromRoot; | |
230 else | |
231 m_renderer.layer()->convertToLayerCoords(context.rootLayer, offset); | |
232 layerBounds = LayoutRect(offset, LayoutSize(m_renderer.layer()->size())); | |
233 | |
234 // Update the clip rects that will be passed to child layers. | |
235 if (m_renderer.hasOverflowClip()) { | |
236 // This layer establishes a clip of some kind. | |
237 if (!isClippingRoot || context.respectOverflowClip == RespectOverflowCli
p) { | |
238 foregroundRect.intersect(toRenderBox(m_renderer).overflowClipRect(of
fset, context.scrollbarRelevancy)); | |
239 if (m_renderer.style()->hasBorderRadius()) | |
240 foregroundRect.setHasRadius(true); | |
241 } | |
242 | |
243 // If we establish an overflow clip at all, then go ahead and make sure
our background | |
244 // rect is intersected with our layer's bounds including our visual over
flow, | |
245 // since any visual overflow like box-shadow or border-outset is not cli
pped by overflow:auto/hidden. | |
246 if (toRenderBox(m_renderer).hasVisualOverflow()) { | |
247 // FIXME: Perhaps we should be propagating the borderbox as the clip
rect for children, even though | |
248 // we may need to inflate our clip specifically for shadows o
r outsets. | |
249 // FIXME: Does not do the right thing with CSS regions yet, since we
don't yet factor in the | |
250 // individual region boxes as overflow. | |
251 LayoutRect layerBoundsWithVisualOverflow = toRenderBox(m_renderer).v
isualOverflowRect(); | |
252 toRenderBox(m_renderer).flipForWritingMode(layerBoundsWithVisualOver
flow); // Layers are in physical coordinates, so the overflow has to be flipped. | |
253 layerBoundsWithVisualOverflow.moveBy(offset); | |
254 if (!isClippingRoot || context.respectOverflowClip == RespectOverflo
wClip) | |
255 backgroundRect.intersect(layerBoundsWithVisualOverflow); | |
256 } else { | |
257 LayoutRect bounds = toRenderBox(m_renderer).borderBoxRect(); | |
258 bounds.moveBy(offset); | |
259 if (!isClippingRoot || context.respectOverflowClip == RespectOverflo
wClip) | |
260 backgroundRect.intersect(bounds); | |
261 } | |
262 } | |
263 | |
264 // CSS clip (different than clipping due to overflow) can clip to any box, e
ven if it falls outside of the border box. | |
265 if (m_renderer.hasClip()) { | |
266 // Clip applies to *us* as well, so go ahead and update the damageRect. | |
267 LayoutRect newPosClip = toRenderBox(m_renderer).clipRect(offset); | |
268 backgroundRect.intersect(newPosClip); | |
269 foregroundRect.intersect(newPosClip); | |
270 outlineRect.intersect(newPosClip); | |
271 } | |
272 } | |
273 | |
274 void RenderLayerClipper::calculateClipRects(const ClipRectsContext& context, Cli
pRects& clipRects) const | |
275 { | |
276 bool rootLayerScrolls = m_renderer.document().settings() && m_renderer.docum
ent().settings()->rootLayerScrolls(); | |
277 if (!m_renderer.layer()->parent() && !rootLayerScrolls) { | |
278 // The root layer's clip rect is always infinite. | |
279 clipRects.reset(LayoutRect::infiniteIntRect()); | |
280 return; | |
281 } | |
282 | |
283 bool isClippingRoot = m_renderer.layer() == context.rootLayer; | |
284 | |
285 // For transformed layers, the root layer was shifted to be us, so there is
no need to | |
286 // examine the parent. We want to cache clip rects with us as the root. | |
287 RenderLayer* parentLayer = !isClippingRoot ? m_renderer.layer()->parent() :
0; | |
288 | |
289 // Ensure that our parent's clip has been calculated so that we can examine
the values. | |
290 if (parentLayer) { | |
291 // FIXME: Why don't we just call getClipRects here? | |
292 if (context.usesCache() && parentLayer->clipper().cachedClipRects(contex
t)) { | |
293 clipRects = *parentLayer->clipper().cachedClipRects(context); | |
294 } else { | |
295 parentLayer->clipper().calculateClipRects(context, clipRects); | |
296 } | |
297 } else { | |
298 clipRects.reset(LayoutRect::infiniteIntRect()); | |
299 } | |
300 | |
301 adjustClipRectsForChildren(m_renderer, clipRects); | |
302 | |
303 if ((m_renderer.hasOverflowClip() && (context.respectOverflowClip == Respect
OverflowClip || !isClippingRoot)) || m_renderer.hasClip()) { | |
304 // This offset cannot use convertToLayerCoords, because sometimes our ro
otLayer may be across | |
305 // some transformed layer boundary, for example, in the RenderLayerCompo
sitor overlapMap, where | |
306 // clipRects are needed in view space. | |
307 applyClipRects(context, m_renderer, roundedLayoutPoint(m_renderer.localT
oContainerPoint(FloatPoint(), context.rootLayer->renderer())), clipRects); | |
308 } | |
309 } | |
310 | |
311 static ClipRect backgroundClipRectForPosition(const ClipRects& parentRects, EPos
ition position) | |
312 { | |
313 if (position == FixedPosition) | |
314 return parentRects.fixedClipRect(); | |
315 | |
316 if (position == AbsolutePosition) | |
317 return parentRects.posClipRect(); | |
318 | |
319 return parentRects.overflowClipRect(); | |
320 } | |
321 | |
322 ClipRect RenderLayerClipper::backgroundClipRect(const ClipRectsContext& context)
const | |
323 { | |
324 ASSERT(m_renderer.layer()->parent()); | |
325 ASSERT(m_renderer.view()); | |
326 | |
327 RefPtr<ClipRects> parentClipRects = ClipRects::create(); | |
328 if (m_renderer.layer() == context.rootLayer) | |
329 parentClipRects->reset(LayoutRect::infiniteIntRect()); | |
330 else | |
331 m_renderer.layer()->parent()->clipper().getOrCalculateClipRects(context,
*parentClipRects); | |
332 | |
333 ClipRect result = backgroundClipRectForPosition(*parentClipRects, m_renderer
.style()->position()); | |
334 | |
335 // Note: infinite clipRects should not be scrolled here, otherwise they will
accidentally no longer be considered infinite. | |
336 if (parentClipRects->fixed() && context.rootLayer->renderer() == m_renderer.
view() && result != LayoutRect::infiniteIntRect()) | |
337 result.move(m_renderer.view()->frameView()->scrollOffsetForViewportConst
rainedObjects()); | |
338 | |
339 return result; | |
340 } | |
341 | |
342 void RenderLayerClipper::getOrCalculateClipRects(const ClipRectsContext& context
, ClipRects& clipRects) const | |
343 { | |
344 if (context.usesCache()) | |
345 clipRects = *getClipRects(context); | |
346 else | |
347 calculateClipRects(context, clipRects); | |
348 } | |
349 | |
350 RenderLayer* RenderLayerClipper::clippingRootForPainting() const | |
351 { | |
352 const RenderLayer* current = m_renderer.layer(); | |
353 // FIXME: getting rid of current->hasCompositedLayerMapping() here breaks th
e | |
354 // compositing/backing/no-backing-for-clip.html layout test, because there i
s a | |
355 // "composited but paints into ancestor" layer involved. However, it doesn't
make sense that | |
356 // that check would be appropriate here but not inside the while loop below. | |
357 if (current->isPaintInvalidationContainer() || current->hasCompositedLayerMa
pping()) | |
358 return const_cast<RenderLayer*>(current); | |
359 | |
360 while (current) { | |
361 if (current->isRootLayer()) | |
362 return const_cast<RenderLayer*>(current); | |
363 | |
364 current = current->compositingContainer(); | |
365 ASSERT(current); | |
366 if (current->transform() || current->isPaintInvalidationContainer()) | |
367 return const_cast<RenderLayer*>(current); | |
368 } | |
369 | |
370 ASSERT_NOT_REACHED(); | |
371 return 0; | |
372 } | |
373 | |
374 } // namespace blink | |
OLD | NEW |