Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) 2004, 2005, 2007, 2008 Rob Buis <buis (at) kde.org>
      4  * Copyright (C) 2007 Eric Seidel <eric (at) webkit.org>
      5  * Copyright (C) 2009 Google, Inc.  All rights reserved.
      6  * Copyright (C) 2009 Dirk Schulze <krit (at) webkit.org>
      7  *
      8  * This library is free software; you can redistribute it and/or
      9  * modify it under the terms of the GNU Library General Public
     10  * License as published by the Free Software Foundation; either
     11  * version 2 of the License, or (at your option) any later version.
     12  *
     13  * This library is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  * Library General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU Library General Public License
     19  * along with this library; see the file COPYING.LIB.  If not, write to
     20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     21  * Boston, MA 02110-1301, USA.
     22  */
     23 
     24 #include "config.h"
     25 
     26 #include "core/rendering/svg/RenderSVGContainer.h"
     27 
     28 #include "core/frame/Settings.h"
     29 #include "core/rendering/GraphicsContextAnnotator.h"
     30 #include "core/rendering/LayoutRepainter.h"
     31 #include "core/rendering/RenderView.h"
     32 #include "core/rendering/svg/SVGRenderSupport.h"
     33 #include "core/rendering/svg/SVGRenderingContext.h"
     34 #include "core/rendering/svg/SVGResources.h"
     35 #include "core/rendering/svg/SVGResourcesCache.h"
     36 #include "platform/graphics/GraphicsContextCullSaver.h"
     37 #include "platform/graphics/GraphicsContextStateSaver.h"
     38 
     39 namespace WebCore {
     40 
     41 RenderSVGContainer::RenderSVGContainer(SVGElement* node)
     42     : RenderSVGModelObject(node)
     43     , m_objectBoundingBoxValid(false)
     44     , m_needsBoundariesUpdate(true)
     45 {
     46 }
     47 
     48 RenderSVGContainer::~RenderSVGContainer()
     49 {
     50 }
     51 
     52 void RenderSVGContainer::layout()
     53 {
     54     ASSERT(needsLayout());
     55 
     56     // RenderSVGRoot disables layoutState for the SVG rendering tree.
     57     ASSERT(!view()->layoutStateCachedOffsetsEnabled());
     58 
     59     LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(this) || selfWillPaint());
     60 
     61     // Allow RenderSVGViewportContainer to update its viewport.
     62     calcViewport();
     63 
     64     // Allow RenderSVGTransformableContainer to update its transform.
     65     bool updatedTransform = calculateLocalTransform();
     66 
     67     // RenderSVGViewportContainer needs to set the 'layout size changed' flag.
     68     determineIfLayoutSizeChanged();
     69 
     70     SVGRenderSupport::layoutChildren(this, selfNeedsLayout() || SVGRenderSupport::filtersForceContainerLayout(this));
     71 
     72     // Invalidate all resources of this client if our layout changed.
     73     if (everHadLayout() && needsLayout())
     74         SVGResourcesCache::clientLayoutChanged(this);
     75 
     76     // At this point LayoutRepainter already grabbed the old bounds,
     77     // recalculate them now so repaintAfterLayout() uses the new bounds.
     78     if (m_needsBoundariesUpdate || updatedTransform) {
     79         updateCachedBoundaries();
     80         m_needsBoundariesUpdate = false;
     81 
     82         // If our bounds changed, notify the parents.
     83         RenderSVGModelObject::setNeedsBoundariesUpdate();
     84     }
     85 
     86     repainter.repaintAfterLayout();
     87     clearNeedsLayout();
     88 }
     89 
     90 void RenderSVGContainer::addChild(RenderObject* child, RenderObject* beforeChild)
     91 {
     92     RenderSVGModelObject::addChild(child, beforeChild);
     93     SVGResourcesCache::clientWasAddedToTree(child, child->style());
     94 }
     95 
     96 void RenderSVGContainer::removeChild(RenderObject* child)
     97 {
     98     SVGResourcesCache::clientWillBeRemovedFromTree(child);
     99     RenderSVGModelObject::removeChild(child);
    100 }
    101 
    102 
    103 bool RenderSVGContainer::selfWillPaint()
    104 {
    105     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
    106     return resources && resources->filter();
    107 }
    108 
    109 void RenderSVGContainer::paint(PaintInfo& paintInfo, const LayoutPoint&)
    110 {
    111     ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
    112 
    113     if (paintInfo.context->paintingDisabled())
    114         return;
    115 
    116     // Spec: groups w/o children still may render filter content.
    117     if (!firstChild() && !selfWillPaint())
    118         return;
    119 
    120     FloatRect repaintRect = paintInvalidationRectInLocalCoordinates();
    121     if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(repaintRect, localToParentTransform(), paintInfo))
    122         return;
    123 
    124     PaintInfo childPaintInfo(paintInfo);
    125     {
    126         GraphicsContextStateSaver stateSaver(*childPaintInfo.context);
    127 
    128         // Let the RenderSVGViewportContainer subclass clip if necessary
    129         applyViewportClip(childPaintInfo);
    130 
    131         childPaintInfo.applyTransform(localToParentTransform());
    132 
    133         SVGRenderingContext renderingContext;
    134         GraphicsContextCullSaver cullSaver(*childPaintInfo.context);
    135         bool continueRendering = true;
    136         if (childPaintInfo.phase == PaintPhaseForeground) {
    137             renderingContext.prepareToRenderSVGContent(this, childPaintInfo);
    138             continueRendering = renderingContext.isRenderingPrepared();
    139 
    140             if (continueRendering && document().settings()->containerCullingEnabled())
    141                 cullSaver.cull(paintInvalidationRectInLocalCoordinates());
    142         }
    143 
    144         if (continueRendering) {
    145             childPaintInfo.updatePaintingRootForChildren(this);
    146             for (RenderObject* child = firstChild(); child; child = child->nextSibling())
    147                 child->paint(childPaintInfo, IntPoint());
    148         }
    149     }
    150 
    151     // FIXME: This really should be drawn from local coordinates, but currently we hack it
    152     // to avoid our clip killing our outline rect. Thus we translate our
    153     // outline rect into parent coords before drawing.
    154     // FIXME: This means our focus ring won't share our rotation like it should.
    155     // We should instead disable our clip during PaintPhaseOutline
    156     if (paintInfo.phase == PaintPhaseForeground && style()->outlineWidth() && style()->visibility() == VISIBLE) {
    157         IntRect paintRectInParent = enclosingIntRect(localToParentTransform().mapRect(repaintRect));
    158         paintOutline(paintInfo, paintRectInParent);
    159     }
    160 }
    161 
    162 // addFocusRingRects is called from paintOutline and needs to be in the same coordinates as the paintOuline call
    163 void RenderSVGContainer::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&, const RenderLayerModelObject*)
    164 {
    165     IntRect paintRectInParent = enclosingIntRect(localToParentTransform().mapRect(paintInvalidationRectInLocalCoordinates()));
    166     if (!paintRectInParent.isEmpty())
    167         rects.append(paintRectInParent);
    168 }
    169 
    170 void RenderSVGContainer::updateCachedBoundaries()
    171 {
    172     SVGRenderSupport::computeContainerBoundingBoxes(this, m_objectBoundingBox, m_objectBoundingBoxValid, m_strokeBoundingBox, m_repaintBoundingBox);
    173     SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
    174 }
    175 
    176 bool RenderSVGContainer::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
    177 {
    178     // Give RenderSVGViewportContainer a chance to apply its viewport clip
    179     if (!pointIsInsideViewportClip(pointInParent))
    180         return false;
    181 
    182     FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
    183 
    184     if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
    185         return false;
    186 
    187     for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
    188         if (child->nodeAtFloatPoint(request, result, localPoint, hitTestAction)) {
    189             updateHitTestResult(result, roundedLayoutPoint(localPoint));
    190             return true;
    191         }
    192     }
    193 
    194     // pointer-events=boundingBox makes it possible for containers to be direct targets
    195     if (style()->pointerEvents() == PE_BOUNDINGBOX) {
    196         ASSERT(isObjectBoundingBoxValid());
    197         if (objectBoundingBox().contains(localPoint)) {
    198             updateHitTestResult(result, roundedLayoutPoint(localPoint));
    199             return true;
    200         }
    201     }
    202     // 16.4: "If there are no graphics elements whose relevant graphics content is under the pointer (i.e., there is no target element), the event is not dispatched."
    203     return false;
    204 }
    205 
    206 }
    207