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 (at) fas.harvard.edu> 9 * Christian Biesinger <cbiesinger (at) web.de> 10 * Randall Jesup <rjesup (at) wgate.com> 11 * Roland Mainz <roland.mainz (at) informatik.med.uni-giessen.de> 12 * Josh Soref <timeless (at) mac.com> 13 * Boris Zbarsky <bzbarsky (at) 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 57 && clipRectsContext.rootLayer == m_clipRectsCache->clipRectsRoot(clipRectsType) 58 && m_clipRectsCache->getClipRects(clipRectsType, clipRectsContext.respectOverflowClip)) { 59 // FIXME: We used to ASSERT that we always got a consistent root layer. 60 // We should add a test that has an inconsistent root. See 61 // http://crbug.com/366118 for an example. 62 ASSERT(m_clipRectsCache->m_scrollbarRelevancy[clipRectsType] == clipRectsContext.overlayScrollbarSizeRelevancy); 63 64 #ifdef CHECK_CACHED_CLIP_RECTS 65 // This code is useful to check cached clip rects, but is too expensive to leave enabled in debug builds by default. 66 ClipRectsContext tempContext(clipRectsContext); 67 tempContext.clipRectsType = TemporaryClipRects; 68 ClipRects clipRects; 69 calculateClipRects(tempContext, clipRects); 70 ASSERT(clipRects == *m_clipRectsCache->getClipRects(clipRectsType, clipRectsContext.respectOverflowClip).get()); 71 #endif 72 return; // We have the correct cached value. 73 } 74 75 // For transformed layers, the root layer was shifted to be us, so there is no need to 76 // examine the parent. We want to cache clip rects with us as the root. 77 RenderLayer* parentLayer = !isClippingRootForContext(clipRectsContext) ? m_renderer.layer()->parent() : 0; 78 if (parentLayer) 79 parentLayer->clipper().updateClipRects(clipRectsContext); 80 81 ClipRects clipRects; 82 calculateClipRects(clipRectsContext, clipRects); 83 84 if (!m_clipRectsCache) 85 m_clipRectsCache = adoptPtr(new ClipRectsCache); 86 87 if (parentLayer && parentLayer->clipper().clipRects(clipRectsContext) && clipRects == *parentLayer->clipper().clipRects(clipRectsContext)) 88 m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, parentLayer->clipper().clipRects(clipRectsContext), clipRectsContext.rootLayer); 89 else 90 m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, ClipRects::create(clipRects), clipRectsContext.rootLayer); 91 92 #ifndef NDEBUG 93 m_clipRectsCache->m_scrollbarRelevancy[clipRectsType] = clipRectsContext.overlayScrollbarSizeRelevancy; 94 #endif 95 } 96 97 void RenderLayerClipper::clearClipRectsIncludingDescendants(ClipRectsType typeToClear) 98 { 99 // FIXME: it's not clear how this layer not having clip rects guarantees that no descendants have any. 100 if (!m_clipRectsCache) 101 return; 102 103 clearClipRects(typeToClear); 104 105 for (RenderLayer* layer = m_renderer.layer()->firstChild(); layer; layer = layer->nextSibling()) 106 layer->clipper().clearClipRectsIncludingDescendants(typeToClear); 107 } 108 109 void RenderLayerClipper::clearClipRects(ClipRectsType typeToClear) 110 { 111 if (typeToClear == AllClipRectTypes) { 112 m_clipRectsCache = nullptr; 113 m_compositingClipRectsDirty = false; 114 } else { 115 if (typeToClear == CompositingClipRects) 116 m_compositingClipRectsDirty = false; 117 118 ASSERT(typeToClear < NumCachedClipRectsTypes); 119 RefPtr<ClipRects> dummy; 120 m_clipRectsCache->setClipRects(typeToClear, RespectOverflowClip, dummy, 0); 121 m_clipRectsCache->setClipRects(typeToClear, IgnoreOverflowClip, dummy, 0); 122 } 123 } 124 125 LayoutRect RenderLayerClipper::childrenClipRect() const 126 { 127 // FIXME: border-radius not accounted for. 128 // FIXME: Regions not accounted for. 129 RenderView* renderView = m_renderer.view(); 130 RenderLayer* clippingRootLayer = clippingRootForPainting(); 131 LayoutRect layerBounds; 132 ClipRect backgroundRect, foregroundRect, outlineRect; 133 ClipRectsContext clipRectsContext(clippingRootLayer, TemporaryClipRects); 134 // Need to use temporary clip rects, because the value of 'dontClipToOverflow' may be different from the painting path (<rdar://problem/11844909>). 135 calculateRects(clipRectsContext, renderView->unscaledDocumentRect(), layerBounds, backgroundRect, foregroundRect, outlineRect); 136 return clippingRootLayer->renderer()->localToAbsoluteQuad(FloatQuad(foregroundRect.rect())).enclosingBoundingBox(); 137 } 138 139 LayoutRect RenderLayerClipper::localClipRect() const 140 { 141 // FIXME: border-radius not accounted for. 142 RenderLayer* clippingRootLayer = clippingRootForPainting(); 143 LayoutRect layerBounds; 144 ClipRect backgroundRect, foregroundRect, outlineRect; 145 ClipRectsContext clipRectsContext(clippingRootLayer, PaintingClipRects); 146 calculateRects(clipRectsContext, PaintInfo::infiniteRect(), layerBounds, backgroundRect, foregroundRect, outlineRect); 147 148 LayoutRect clipRect = backgroundRect.rect(); 149 if (clipRect == PaintInfo::infiniteRect()) 150 return clipRect; 151 152 LayoutPoint clippingRootOffset; 153 m_renderer.layer()->convertToLayerCoords(clippingRootLayer, clippingRootOffset); 154 clipRect.moveBy(-clippingRootOffset); 155 156 return clipRect; 157 } 158 159 void RenderLayerClipper::calculateRects(const ClipRectsContext& clipRectsContext, const LayoutRect& paintDirtyRect, LayoutRect& layerBounds, 160 ClipRect& backgroundRect, ClipRect& foregroundRect, ClipRect& outlineRect, const LayoutPoint* offsetFromRoot) const 161 { 162 bool isClippingRoot = isClippingRootForContext(clipRectsContext); 163 164 if (!isClippingRoot && m_renderer.layer()->parent()) { 165 backgroundRect = backgroundClipRect(clipRectsContext); 166 backgroundRect.move(roundedIntSize(clipRectsContext.subPixelAccumulation)); 167 backgroundRect.intersect(paintDirtyRect); 168 } else { 169 backgroundRect = paintDirtyRect; 170 } 171 172 foregroundRect = backgroundRect; 173 outlineRect = backgroundRect; 174 175 LayoutPoint offset; 176 if (offsetFromRoot) 177 offset = *offsetFromRoot; 178 else 179 m_renderer.layer()->convertToLayerCoords(clipRectsContext.rootLayer, offset); 180 layerBounds = LayoutRect(offset, m_renderer.layer()->size()); 181 182 // Update the clip rects that will be passed to child layers. 183 if (m_renderer.hasOverflowClip()) { 184 // This layer establishes a clip of some kind. 185 if (!isClippingRoot || clipRectsContext.respectOverflowClip == RespectOverflowClip) { 186 foregroundRect.intersect(toRenderBox(m_renderer).overflowClipRect(offset, clipRectsContext.overlayScrollbarSizeRelevancy)); 187 if (m_renderer.style()->hasBorderRadius()) 188 foregroundRect.setHasRadius(true); 189 } 190 191 // If we establish an overflow clip at all, then go ahead and make sure our background 192 // rect is intersected with our layer's bounds including our visual overflow, 193 // since any visual overflow like box-shadow or border-outset is not clipped by overflow:auto/hidden. 194 if (toRenderBox(m_renderer).hasVisualOverflow()) { 195 // FIXME: Perhaps we should be propagating the borderbox as the clip rect for children, even though 196 // we may need to inflate our clip specifically for shadows or outsets. 197 // FIXME: Does not do the right thing with CSS regions yet, since we don't yet factor in the 198 // individual region boxes as overflow. 199 LayoutRect layerBoundsWithVisualOverflow = toRenderBox(m_renderer).visualOverflowRect(); 200 toRenderBox(m_renderer).flipForWritingMode(layerBoundsWithVisualOverflow); // Layers are in physical coordinates, so the overflow has to be flipped. 201 layerBoundsWithVisualOverflow.moveBy(offset); 202 if (!isClippingRoot || clipRectsContext.respectOverflowClip == RespectOverflowClip) 203 backgroundRect.intersect(layerBoundsWithVisualOverflow); 204 } else { 205 LayoutRect bounds = toRenderBox(m_renderer).borderBoxRect(); 206 bounds.moveBy(offset); 207 if (!isClippingRoot || clipRectsContext.respectOverflowClip == RespectOverflowClip) 208 backgroundRect.intersect(bounds); 209 } 210 } 211 212 // CSS clip (different than clipping due to overflow) can clip to any box, even if it falls outside of the border box. 213 if (m_renderer.hasClip()) { 214 // Clip applies to *us* as well, so go ahead and update the damageRect. 215 LayoutRect newPosClip = toRenderBox(m_renderer).clipRect(offset); 216 backgroundRect.intersect(newPosClip); 217 foregroundRect.intersect(newPosClip); 218 outlineRect.intersect(newPosClip); 219 } 220 } 221 222 void RenderLayerClipper::calculateClipRects(const ClipRectsContext& clipRectsContext, ClipRects& clipRects) const 223 { 224 if (!m_renderer.layer()->parent()) { 225 // The root layer's clip rect is always infinite. 226 clipRects.reset(PaintInfo::infiniteRect()); 227 return; 228 } 229 230 ClipRectsType clipRectsType = clipRectsContext.clipRectsType; 231 bool useCached = clipRectsType != TemporaryClipRects; 232 233 bool isClippingRoot = isClippingRootForContext(clipRectsContext); 234 235 // For transformed layers, the root layer was shifted to be us, so there is no need to 236 // examine the parent. We want to cache clip rects with us as the root. 237 RenderLayer* parentLayer = !isClippingRoot ? m_renderer.layer()->parent() : 0; 238 239 // Ensure that our parent's clip has been calculated so that we can examine the values. 240 if (parentLayer) { 241 if (useCached && parentLayer->clipper().clipRects(clipRectsContext)) { 242 clipRects = *parentLayer->clipper().clipRects(clipRectsContext); 243 } else { 244 ClipRectsContext parentContext(clipRectsContext); 245 parentContext.overlayScrollbarSizeRelevancy = IgnoreOverlayScrollbarSize; // FIXME: why? 246 parentLayer->clipper().calculateClipRects(parentContext, clipRects); 247 } 248 } else { 249 clipRects.reset(PaintInfo::infiniteRect()); 250 } 251 252 // A fixed object is essentially the root of its containing block hierarchy, so when 253 // we encounter such an object, we reset our clip rects to the fixedClipRect. 254 if (m_renderer.style()->position() == FixedPosition) { 255 clipRects.setPosClipRect(clipRects.fixedClipRect()); 256 clipRects.setOverflowClipRect(clipRects.fixedClipRect()); 257 clipRects.setFixed(true); 258 } else if (m_renderer.style()->hasInFlowPosition()) { 259 clipRects.setPosClipRect(clipRects.overflowClipRect()); 260 } else if (m_renderer.style()->position() == AbsolutePosition) { 261 clipRects.setOverflowClipRect(clipRects.posClipRect()); 262 } 263 264 // Update the clip rects that will be passed to child layers. 265 if ((m_renderer.hasOverflowClip() && (clipRectsContext.respectOverflowClip == RespectOverflowClip || !isClippingRoot)) || m_renderer.hasClip()) { 266 // This layer establishes a clip of some kind. 267 268 // This offset cannot use convertToLayerCoords, because sometimes our rootLayer may be across 269 // some transformed layer boundary, for example, in the RenderLayerCompositor overlapMap, where 270 // clipRects are needed in view space. 271 LayoutPoint offset; 272 offset = roundedLayoutPoint(m_renderer.localToContainerPoint(FloatPoint(), clipRectsContext.rootLayer->renderer())); 273 RenderView* view = m_renderer.view(); 274 ASSERT(view); 275 if (view && clipRects.fixed() && clipRectsContext.rootLayer->renderer() == view) { 276 offset -= view->frameView()->scrollOffsetForFixedPosition(); 277 } 278 279 if (m_renderer.hasOverflowClip()) { 280 ClipRect newOverflowClip = toRenderBox(m_renderer).overflowClipRect(offset, clipRectsContext.overlayScrollbarSizeRelevancy); 281 if (m_renderer.style()->hasBorderRadius()) 282 newOverflowClip.setHasRadius(true); 283 clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect())); 284 if (m_renderer.isPositioned()) 285 clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect())); 286 } 287 if (m_renderer.hasClip()) { 288 LayoutRect newPosClip = toRenderBox(m_renderer).clipRect(offset); 289 clipRects.setPosClipRect(intersection(newPosClip, clipRects.posClipRect())); 290 clipRects.setOverflowClipRect(intersection(newPosClip, clipRects.overflowClipRect())); 291 clipRects.setFixedClipRect(intersection(newPosClip, clipRects.fixedClipRect())); 292 } 293 } 294 } 295 296 static inline ClipRect backgroundClipRectForPosition(const ClipRects& parentRects, EPosition position) 297 { 298 if (position == FixedPosition) 299 return parentRects.fixedClipRect(); 300 301 if (position == AbsolutePosition) 302 return parentRects.posClipRect(); 303 304 return parentRects.overflowClipRect(); 305 } 306 307 void RenderLayerClipper::setCompositingClipRectsDirty() 308 { 309 m_compositingClipRectsDirty = true; 310 } 311 312 ClipRect RenderLayerClipper::backgroundClipRect(const ClipRectsContext& clipRectsContext) const 313 { 314 ASSERT(m_renderer.layer()->parent()); 315 316 if (clipRectsContext.clipRectsType == CompositingClipRects) 317 const_cast<RenderLayerClipper*>(this)->clearClipRectsIncludingDescendants(CompositingClipRects); 318 319 ClipRects parentRects; 320 321 // If we cross into a different pagination context, then we can't rely on the cache. 322 // Just switch over to using TemporaryClipRects. 323 if (clipRectsContext.clipRectsType != TemporaryClipRects && m_renderer.layer()->parent()->enclosingPaginationLayer() != m_renderer.layer()->enclosingPaginationLayer()) { 324 ClipRectsContext tempContext(clipRectsContext); 325 tempContext.clipRectsType = TemporaryClipRects; 326 parentClipRects(tempContext, parentRects); 327 } else { 328 parentClipRects(clipRectsContext, parentRects); 329 } 330 331 ClipRect backgroundClipRect = backgroundClipRectForPosition(parentRects, m_renderer.style()->position()); 332 RenderView* view = m_renderer.view(); 333 ASSERT(view); 334 335 // Note: infinite clipRects should not be scrolled here, otherwise they will accidentally no longer be considered infinite. 336 if (parentRects.fixed() && clipRectsContext.rootLayer->renderer() == view && backgroundClipRect != PaintInfo::infiniteRect()) 337 backgroundClipRect.move(view->frameView()->scrollOffsetForFixedPosition()); 338 339 return backgroundClipRect; 340 } 341 342 bool RenderLayerClipper::isClippingRootForContext(const ClipRectsContext& clipRectsContext) const 343 { 344 return clipRectsContext.rootLayer == m_renderer.layer(); 345 } 346 347 void RenderLayerClipper::parentClipRects(const ClipRectsContext& clipRectsContext, ClipRects& clipRects) const 348 { 349 // The root is not clipped. 350 if (isClippingRootForContext(clipRectsContext)) { 351 clipRects.reset(PaintInfo::infiniteRect()); 352 return; 353 } 354 355 ASSERT(m_renderer.layer()->parent()); 356 357 RenderLayerClipper& parentClipper = m_renderer.layer()->parent()->clipper(); 358 if (clipRectsContext.clipRectsType == TemporaryClipRects) { 359 parentClipper.calculateClipRects(clipRectsContext, clipRects); 360 return; 361 } 362 363 parentClipper.updateClipRects(clipRectsContext); 364 clipRects = *parentClipper.clipRects(clipRectsContext); 365 } 366 367 RenderLayer* RenderLayerClipper::clippingRootForPainting() const 368 { 369 if (m_renderer.hasCompositedLayerMapping() || m_renderer.groupedMapping()) 370 return const_cast<RenderLayer*>(m_renderer.layer()); 371 372 const RenderLayer* current = m_renderer.layer(); 373 while (current) { 374 if (current->isRootLayer()) 375 return const_cast<RenderLayer*>(current); 376 377 current = current->compositingContainer(); 378 ASSERT(current); 379 if (current->transform() || (current->compositingState() == PaintsIntoOwnBacking) || current->groupedMapping()) 380 return const_cast<RenderLayer*>(current); 381 } 382 383 ASSERT_NOT_REACHED(); 384 return 0; 385 } 386 387 } // namespace WebCore 388