Home | History | Annotate | Download | only in rendering
      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