CTTE: SVGResourcesCache should only allow RenderElements.
[WebKit-https.git] / Source / WebCore / rendering / svg / RenderSVGImage.cpp
1 /*
2  * Copyright (C) 2006 Alexander Kellett <lypanov@kde.org>
3  * Copyright (C) 2006 Apple Inc.
4  * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
5  * Copyright (C) 2007, 2008, 2009 Rob Buis <buis@kde.org>
6  * Copyright (C) 2009 Google, Inc.
7  * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
8  * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25
26 #include "config.h"
27 #include "RenderSVGImage.h"
28
29 #include "Attr.h"
30 #include "FloatQuad.h"
31 #include "GraphicsContext.h"
32 #include "LayoutRepainter.h"
33 #include "PointerEventsHitRules.h"
34 #include "RenderImageResource.h"
35 #include "RenderLayer.h"
36 #include "RenderSVGResource.h"
37 #include "RenderSVGResourceFilter.h"
38 #include "SVGImageElement.h"
39 #include "SVGLength.h"
40 #include "SVGRenderingContext.h"
41 #include "SVGResources.h"
42 #include "SVGResourcesCache.h"
43 #include <wtf/StackStats.h>
44
45 namespace WebCore {
46
47 RenderSVGImage::RenderSVGImage(SVGImageElement& element, PassRef<RenderStyle> style)
48     : RenderSVGModelObject(element, WTF::move(style))
49     , m_needsBoundariesUpdate(true)
50     , m_needsTransformUpdate(true)
51     , m_imageResource(std::make_unique<RenderImageResource>())
52 {
53     imageResource().initialize(this);
54 }
55
56 RenderSVGImage::~RenderSVGImage()
57 {
58     imageResource().shutdown();
59 }
60
61 SVGImageElement& RenderSVGImage::imageElement() const
62 {
63     return toSVGImageElement(RenderSVGModelObject::element());
64 }
65
66 bool RenderSVGImage::updateImageViewport()
67 {
68     FloatRect oldBoundaries = m_objectBoundingBox;
69     bool updatedViewport = false;
70
71     SVGLengthContext lengthContext(&imageElement());
72     m_objectBoundingBox = FloatRect(imageElement().x().value(lengthContext), imageElement().y().value(lengthContext), imageElement().width().value(lengthContext), imageElement().height().value(lengthContext));
73
74     // Images with preserveAspectRatio=none should force non-uniform scaling. This can be achieved
75     // by setting the image's container size to its intrinsic size.
76     // See: http://www.w3.org/TR/SVG/single-page.html, 7.8 The â€˜preserveAspectRatio’ attribute.
77     if (imageElement().preserveAspectRatio().align() == SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE) {
78         if (CachedImage* cachedImage = imageResource().cachedImage()) {
79             LayoutSize intrinsicSize = cachedImage->imageSizeForRenderer(0, style().effectiveZoom());
80             if (intrinsicSize != imageResource().imageSize(style().effectiveZoom())) {
81                 imageResource().setContainerSizeForRenderer(roundedIntSize(intrinsicSize));
82                 updatedViewport = true;
83             }
84         }
85     }
86
87     if (oldBoundaries != m_objectBoundingBox) {
88         if (!updatedViewport)
89             imageResource().setContainerSizeForRenderer(enclosingIntRect(m_objectBoundingBox).size());
90         updatedViewport = true;
91         m_needsBoundariesUpdate = true;
92     }
93
94     return updatedViewport;
95 }
96
97 void RenderSVGImage::layout()
98 {
99     StackStats::LayoutCheckPoint layoutCheckPoint;
100     ASSERT(needsLayout());
101
102     LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(*this) && selfNeedsLayout());
103     updateImageViewport();
104
105     bool transformOrBoundariesUpdate = m_needsTransformUpdate || m_needsBoundariesUpdate;
106     if (m_needsTransformUpdate) {
107         m_localTransform = imageElement().animatedLocalTransform();
108         m_needsTransformUpdate = false;
109     }
110
111     if (m_needsBoundariesUpdate) {
112         m_repaintBoundingBoxExcludingShadow = m_objectBoundingBox;
113         SVGRenderSupport::intersectRepaintRectWithResources(*this, m_repaintBoundingBoxExcludingShadow);
114
115         m_repaintBoundingBox = m_repaintBoundingBoxExcludingShadow;
116         SVGRenderSupport::intersectRepaintRectWithShadows(*this, m_repaintBoundingBox);
117
118         m_needsBoundariesUpdate = false;
119     }
120
121     // Invalidate all resources of this client if our layout changed.
122     if (everHadLayout() && selfNeedsLayout())
123         SVGResourcesCache::clientLayoutChanged(*this);
124
125     // If our bounds changed, notify the parents.
126     if (transformOrBoundariesUpdate)
127         RenderSVGModelObject::setNeedsBoundariesUpdate();
128
129     repainter.repaintAfterLayout();
130     clearNeedsLayout();
131 }
132
133 void RenderSVGImage::paint(PaintInfo& paintInfo, const LayoutPoint&)
134 {
135     if (paintInfo.context->paintingDisabled() || paintInfo.phase != PaintPhaseForeground
136         || style().visibility() == HIDDEN || !imageResource().hasImage())
137         return;
138
139     FloatRect boundingBox = repaintRectInLocalCoordinates();
140     if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
141         return;
142
143     PaintInfo childPaintInfo(paintInfo);
144     GraphicsContextStateSaver stateSaver(*childPaintInfo.context);
145     childPaintInfo.applyTransform(m_localTransform);
146
147     if (childPaintInfo.phase == PaintPhaseForeground) {
148         SVGRenderingContext renderingContext(*this, childPaintInfo);
149
150         if (renderingContext.isRenderingPrepared()) {
151             if (style().svgStyle().bufferedRendering() == BR_STATIC  && renderingContext.bufferForeground(m_bufferedForeground))
152                 return;
153
154             paintForeground(childPaintInfo);
155         }
156     }
157
158     if (style().outlineWidth())
159         paintOutline(childPaintInfo, IntRect(boundingBox));
160 }
161
162 void RenderSVGImage::paintForeground(PaintInfo& paintInfo)
163 {
164     RefPtr<Image> image = imageResource().image();
165     FloatRect destRect = m_objectBoundingBox;
166     FloatRect srcRect(0, 0, image->width(), image->height());
167
168     imageElement().preserveAspectRatio().transformRect(destRect, srcRect);
169
170     paintInfo.context->drawImage(image.get(), ColorSpaceDeviceRGB, destRect, srcRect);
171 }
172
173 void RenderSVGImage::invalidateBufferedForeground()
174 {
175     m_bufferedForeground.reset();
176 }
177
178 bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
179 {
180     // We only draw in the forground phase, so we only hit-test then.
181     if (hitTestAction != HitTestForeground)
182         return false;
183
184     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, request, style().pointerEvents());
185     bool isVisible = (style().visibility() == VISIBLE);
186     if (isVisible || !hitRules.requireVisible) {
187         FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
188             
189         if (!SVGRenderSupport::pointInClippingArea(*this, localPoint))
190             return false;
191
192         if (hitRules.canHitFill) {
193             if (m_objectBoundingBox.contains(localPoint)) {
194                 updateHitTestResult(result, LayoutPoint(localPoint));
195                 return true;
196             }
197         }
198     }
199
200     return false;
201 }
202
203 void RenderSVGImage::imageChanged(WrappedImagePtr, const IntRect*)
204 {
205     // The image resource defaults to nullImage until the resource arrives.
206     // This empty image may be cached by SVG resources which must be invalidated.
207     if (auto* resources = SVGResourcesCache::cachedResourcesForRenderer(*this))
208         resources->removeClientFromCache(*this);
209
210     // Eventually notify parent resources, that we've changed.
211     RenderSVGResource::markForLayoutAndParentResourceInvalidation(*this, false);
212
213     // Update the SVGImageCache sizeAndScales entry in case image loading finished after layout.
214     // (https://bugs.webkit.org/show_bug.cgi?id=99489)
215     m_objectBoundingBox = FloatRect();
216     updateImageViewport();
217
218     invalidateBufferedForeground();
219
220     repaint();
221 }
222
223 void RenderSVGImage::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&, const RenderLayerModelObject*)
224 {
225     // this is called from paint() after the localTransform has already been applied
226     IntRect contentRect = enclosingIntRect(repaintRectInLocalCoordinates());
227     if (!contentRect.isEmpty())
228         rects.append(contentRect);
229 }
230
231 } // namespace WebCore