| 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 |