Remove "virtual" from all lines that have both "virtual" and "override".
[WebKit-https.git] / Source / WebCore / svg / graphics / SVGImage.cpp
1 /*
2  * Copyright (C) 2006 Eric Seidel <eric@webkit.org>
3  * Copyright (C) 2008, 2009, 2015 Apple Inc. All rights reserved.
4  * Copyright (C) Research In Motion Limited 2011. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
26  */
27
28 #include "config.h"
29 #include "SVGImage.h"
30
31 #include "Chrome.h"
32 #include "DocumentLoader.h"
33 #include "ElementIterator.h"
34 #include "FrameLoader.h"
35 #include "FrameView.h"
36 #include "ImageBuffer.h"
37 #include "ImageObserver.h"
38 #include "IntRect.h"
39 #include "MainFrame.h"
40 #include "PageConfiguration.h"
41 #include "RenderSVGRoot.h"
42 #include "RenderStyle.h"
43 #include "SVGDocument.h"
44 #include "SVGFEImageElement.h"
45 #include "SVGForeignObjectElement.h"
46 #include "SVGImageClients.h"
47 #include "SVGImageElement.h"
48 #include "SVGSVGElement.h"
49 #include "Settings.h"
50 #include "TextStream.h"
51
52 namespace WebCore {
53
54 SVGImage::SVGImage(ImageObserver& observer, const URL& url)
55     : Image(&observer)
56     , m_url(url)
57 {
58 }
59
60 SVGImage::~SVGImage()
61 {
62     if (m_page) {
63         // Store m_page in a local variable, clearing m_page, so that SVGImageChromeClient knows we're destructed.
64         std::unique_ptr<Page> currentPage = WTFMove(m_page);
65         currentPage->mainFrame().loader().frameDetached(); // Break both the loader and view references to the frame
66     }
67
68     // Verify that page teardown destroyed the Chrome
69     ASSERT(!m_chromeClient || !m_chromeClient->image());
70 }
71
72 inline SVGSVGElement* SVGImage::rootElement() const
73 {
74     if (!m_page)
75         return nullptr;
76     return downcast<SVGDocument>(*m_page->mainFrame().document()).rootElement();
77 }
78
79 bool SVGImage::hasSingleSecurityOrigin() const
80 {
81     SVGSVGElement* rootElement = this->rootElement();
82     if (!rootElement)
83         return true;
84
85     // FIXME: Once foreignObject elements within SVG images are updated to not leak cross-origin data
86     // (e.g., visited links, spellcheck) we can remove the SVGForeignObjectElement check here and
87     // research if we can remove the Image::hasSingleSecurityOrigin mechanism entirely.
88     for (auto& element : descendantsOfType<SVGElement>(*rootElement)) {
89         if (is<SVGForeignObjectElement>(element))
90             return false;
91         if (is<SVGImageElement>(element)) {
92             if (!downcast<SVGImageElement>(element).hasSingleSecurityOrigin())
93                 return false;
94         } else if (is<SVGFEImageElement>(element)) {
95             if (!downcast<SVGFEImageElement>(element).hasSingleSecurityOrigin())
96                 return false;
97         }
98     }
99
100     // Because SVG image rendering disallows external resources and links,
101     // these images effectively are restricted to a single security origin.
102     return true;
103 }
104
105 void SVGImage::setContainerSize(const FloatSize& size)
106 {
107     if (!usesContainerSize())
108         return;
109
110     SVGSVGElement* rootElement = this->rootElement();
111     if (!rootElement)
112         return;
113     auto* renderer = downcast<RenderSVGRoot>(rootElement->renderer());
114     if (!renderer)
115         return;
116
117     FrameView* view = frameView();
118     view->resize(this->containerSize());
119
120     renderer->setContainerSize(IntSize(size));
121 }
122
123 IntSize SVGImage::containerSize() const
124 {
125     SVGSVGElement* rootElement = this->rootElement();
126     if (!rootElement)
127         return IntSize();
128
129     auto* renderer = downcast<RenderSVGRoot>(rootElement->renderer());
130     if (!renderer)
131         return IntSize();
132
133     // If a container size is available it has precedence.
134     IntSize containerSize = renderer->containerSize();
135     if (!containerSize.isEmpty())
136         return containerSize;
137
138     // Assure that a container size is always given for a non-identity zoom level.
139     ASSERT(renderer->style().effectiveZoom() == 1);
140
141     FloatSize currentSize;
142     if (rootElement->hasIntrinsicWidth() && rootElement->hasIntrinsicHeight())
143         currentSize = rootElement->currentViewportSize();
144     else
145         currentSize = rootElement->currentViewBoxRect().size();
146
147     if (!currentSize.isEmpty())
148         return IntSize(static_cast<int>(ceilf(currentSize.width())), static_cast<int>(ceilf(currentSize.height())));
149
150     // As last resort, use CSS default intrinsic size.
151     return IntSize(300, 150);
152 }
153
154 void SVGImage::drawForContainer(GraphicsContext& context, const FloatSize containerSize, float zoom, const FloatRect& dstRect,
155     const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode)
156 {
157     if (!m_page)
158         return;
159
160     ImageObserver* observer = imageObserver();
161     ASSERT(observer);
162
163     // Temporarily reset image observer, we don't want to receive any changeInRect() calls due to this relayout.
164     setImageObserver(nullptr);
165
166     IntSize roundedContainerSize = roundedIntSize(containerSize);
167     setContainerSize(roundedContainerSize);
168
169     FloatRect scaledSrc = srcRect;
170     scaledSrc.scale(1 / zoom);
171
172     // Compensate for the container size rounding by adjusting the source rect.
173     FloatSize adjustedSrcSize = scaledSrc.size();
174     adjustedSrcSize.scale(roundedContainerSize.width() / containerSize.width(), roundedContainerSize.height() / containerSize.height());
175     scaledSrc.setSize(adjustedSrcSize);
176
177     draw(context, dstRect, scaledSrc, compositeOp, blendMode, ImageOrientationDescription());
178
179     setImageObserver(observer);
180 }
181
182 #if USE(CAIRO)
183 // Passes ownership of the native image to the caller so PassNativeImagePtr needs
184 // to be a smart pointer type.
185 PassNativeImagePtr SVGImage::nativeImageForCurrentFrame()
186 {
187     if (!m_page)
188         return 0;
189
190     // Cairo does not use the accelerated drawing flag, so it's OK to make an unconditionally unaccelerated buffer.
191     std::unique_ptr<ImageBuffer> buffer = ImageBuffer::create(size(), Unaccelerated);
192     if (!buffer) // failed to allocate image
193         return 0;
194
195     draw(buffer->context(), rect(), rect(), CompositeSourceOver, BlendModeNormal, ImageOrientationDescription());
196
197     // FIXME: WK(Bug 113657): We should use DontCopyBackingStore here.
198     return buffer->copyImage(CopyBackingStore)->nativeImageForCurrentFrame();
199 }
200 #endif
201
202 void SVGImage::drawPatternForContainer(GraphicsContext& context, const FloatSize& containerSize, float zoom, const FloatRect& srcRect,
203     const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator compositeOp, const FloatRect& dstRect, BlendMode blendMode)
204 {
205     FloatRect zoomedContainerRect = FloatRect(FloatPoint(), containerSize);
206     zoomedContainerRect.scale(zoom);
207
208     // The ImageBuffer size needs to be scaled to match the final resolution.
209     AffineTransform transform = context.getCTM();
210     FloatSize imageBufferScale = FloatSize(transform.xScale(), transform.yScale());
211     ASSERT(imageBufferScale.width());
212     ASSERT(imageBufferScale.height());
213
214     FloatRect imageBufferSize = zoomedContainerRect;
215     imageBufferSize.scale(imageBufferScale.width(), imageBufferScale.height());
216
217     std::unique_ptr<ImageBuffer> buffer = ImageBuffer::createCompatibleBuffer(expandedIntSize(imageBufferSize.size()), 1, ColorSpaceSRGB, context, true);
218     if (!buffer) // Failed to allocate buffer.
219         return;
220     drawForContainer(buffer->context(), containerSize, zoom, imageBufferSize, zoomedContainerRect, CompositeSourceOver, BlendModeNormal);
221     if (context.drawLuminanceMask())
222         buffer->convertToLuminanceMask();
223
224     RefPtr<Image> image = ImageBuffer::sinkIntoImage(WTFMove(buffer), Unscaled);
225     if (!image)
226         return;
227
228     // Adjust the source rect and transform due to the image buffer's scaling.
229     FloatRect scaledSrcRect = srcRect;
230     scaledSrcRect.scale(imageBufferScale.width(), imageBufferScale.height());
231     AffineTransform unscaledPatternTransform(patternTransform);
232     unscaledPatternTransform.scale(1 / imageBufferScale.width(), 1 / imageBufferScale.height());
233
234     context.setDrawLuminanceMask(false);
235     image->drawPattern(context, scaledSrcRect, unscaledPatternTransform, phase, spacing, compositeOp, dstRect, blendMode);
236 }
237
238 void SVGImage::draw(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription)
239 {
240     if (!m_page)
241         return;
242
243     FrameView* view = frameView();
244     ASSERT(view);
245
246     GraphicsContextStateSaver stateSaver(context);
247     context.setCompositeOperation(compositeOp, blendMode);
248     context.clip(enclosingIntRect(dstRect));
249
250     float alpha = context.alpha();
251     bool compositingRequiresTransparencyLayer = compositeOp != CompositeSourceOver || blendMode != BlendModeNormal || alpha < 1;
252     if (compositingRequiresTransparencyLayer) {
253         context.beginTransparencyLayer(alpha);
254         context.setCompositeOperation(CompositeSourceOver, BlendModeNormal);
255     }
256
257     FloatSize scale(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height());
258     
259     // We can only draw the entire frame, clipped to the rect we want. So compute where the top left
260     // of the image would be if we were drawing without clipping, and translate accordingly.
261     FloatSize topLeftOffset(srcRect.location().x() * scale.width(), srcRect.location().y() * scale.height());
262     FloatPoint destOffset = dstRect.location() - topLeftOffset;
263
264     context.translate(destOffset.x(), destOffset.y());
265     context.scale(scale);
266
267     view->resize(containerSize());
268
269     if (!m_url.isEmpty())
270         view->scrollToFragment(m_url);
271     
272     if (view->needsLayout())
273         view->layout();
274
275     view->paint(context, intersection(context.clipBounds(), enclosingIntRect(srcRect)));
276
277     if (compositingRequiresTransparencyLayer)
278         context.endTransparencyLayer();
279
280     stateSaver.restore();
281
282     if (imageObserver())
283         imageObserver()->didDraw(this);
284 }
285
286 RenderBox* SVGImage::embeddedContentBox() const
287 {
288     SVGSVGElement* rootElement = this->rootElement();
289     if (!rootElement)
290         return nullptr;
291     return downcast<RenderBox>(rootElement->renderer());
292 }
293
294 FrameView* SVGImage::frameView() const
295 {
296     if (!m_page)
297         return nullptr;
298     return m_page->mainFrame().view();
299 }
300
301 bool SVGImage::hasRelativeWidth() const
302 {
303     SVGSVGElement* rootElement = this->rootElement();
304     if (!rootElement)
305         return false;
306     return rootElement->intrinsicWidth().isPercentOrCalculated();
307 }
308
309 bool SVGImage::hasRelativeHeight() const
310 {
311     SVGSVGElement* rootElement = this->rootElement();
312     if (!rootElement)
313         return false;
314     return rootElement->intrinsicHeight().isPercentOrCalculated();
315 }
316
317 void SVGImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
318 {
319     SVGSVGElement* rootElement = this->rootElement();
320     if (!rootElement)
321         return;
322
323     intrinsicWidth = rootElement->intrinsicWidth();
324     intrinsicHeight = rootElement->intrinsicHeight();
325     if (rootElement->preserveAspectRatio().align() == SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE)
326         return;
327
328     intrinsicRatio = rootElement->viewBox().size();
329     if (intrinsicRatio.isEmpty() && intrinsicWidth.isFixed() && intrinsicHeight.isFixed())
330         intrinsicRatio = FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0));
331 }
332
333 // FIXME: support catchUpIfNecessary.
334 void SVGImage::startAnimation(CatchUpAnimation)
335 {
336     SVGSVGElement* rootElement = this->rootElement();
337     if (!rootElement)
338         return;
339     rootElement->unpauseAnimations();
340     rootElement->setCurrentTime(0);
341 }
342
343 void SVGImage::stopAnimation()
344 {
345     SVGSVGElement* rootElement = this->rootElement();
346     if (!rootElement)
347         return;
348     rootElement->pauseAnimations();
349 }
350
351 void SVGImage::resetAnimation()
352 {
353     stopAnimation();
354 }
355
356 bool SVGImage::dataChanged(bool allDataReceived)
357 {
358     // Don't do anything if is an empty image.
359     if (!data()->size())
360         return true;
361
362     if (allDataReceived) {
363         PageConfiguration pageConfiguration;
364         fillWithEmptyClients(pageConfiguration);
365         m_chromeClient = std::make_unique<SVGImageChromeClient>(this);
366         pageConfiguration.chromeClient = m_chromeClient.get();
367
368         // FIXME: If this SVG ends up loading itself, we might leak the world.
369         // The Cache code does not know about CachedImages holding Frames and
370         // won't know to break the cycle.
371         // This will become an issue when SVGImage will be able to load other
372         // SVGImage objects, but we're safe now, because SVGImage can only be
373         // loaded by a top-level document.
374         m_page = std::make_unique<Page>(pageConfiguration);
375         m_page->settings().setMediaEnabled(false);
376         m_page->settings().setScriptEnabled(false);
377         m_page->settings().setPluginsEnabled(false);
378
379         Frame& frame = m_page->mainFrame();
380         frame.setView(FrameView::create(frame));
381         frame.init();
382         FrameLoader& loader = frame.loader();
383         loader.forceSandboxFlags(SandboxAll);
384
385         frame.view()->setCanHaveScrollbars(false); // SVG Images will always synthesize a viewBox, if it's not available, and thus never see scrollbars.
386         frame.view()->setTransparent(true); // SVG Images are transparent.
387
388         ASSERT(loader.activeDocumentLoader()); // DocumentLoader should have been created by frame->init().
389         loader.activeDocumentLoader()->writer().setMIMEType("image/svg+xml");
390         loader.activeDocumentLoader()->writer().begin(URL()); // create the empty document
391         loader.activeDocumentLoader()->writer().addData(data()->data(), data()->size());
392         loader.activeDocumentLoader()->writer().end();
393
394         // Set the intrinsic size before a container size is available.
395         m_intrinsicSize = containerSize();
396     }
397
398     return m_page != nullptr;
399 }
400
401 String SVGImage::filenameExtension() const
402 {
403     return "svg";
404 }
405
406 bool isInSVGImage(const Element* element)
407 {
408     ASSERT(element);
409
410     Page* page = element->document().page();
411     if (!page)
412         return false;
413
414     return page->chrome().client().isSVGImageChromeClient();
415 }
416
417 void SVGImage::dump(TextStream& ts) const
418 {
419     Image::dump(ts);
420     ts.dumpProperty("url", m_url.string());
421 }
422
423
424 }