Add WebSocketProvider stub
[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 "DOMWindow.h"
33 #include "DocumentLoader.h"
34 #include "ElementIterator.h"
35 #include "FrameLoader.h"
36 #include "FrameView.h"
37 #include "ImageBuffer.h"
38 #include "ImageObserver.h"
39 #include "IntRect.h"
40 #include "JSDOMWindowBase.h"
41 #include "MainFrame.h"
42 #include "Page.h"
43 #include "PageConfiguration.h"
44 #include "RenderSVGRoot.h"
45 #include "RenderStyle.h"
46 #include "SVGDocument.h"
47 #include "SVGFEImageElement.h"
48 #include "SVGForeignObjectElement.h"
49 #include "SVGImageClients.h"
50 #include "SVGImageElement.h"
51 #include "SVGSVGElement.h"
52 #include "Settings.h"
53 #include "TextStream.h"
54 #include <runtime/JSCInlines.h>
55 #include <runtime/JSLock.h>
56
57 namespace WebCore {
58
59 SVGImage::SVGImage(ImageObserver& observer, const URL& url)
60     : Image(&observer)
61     , m_url(url)
62 {
63 }
64
65 SVGImage::~SVGImage()
66 {
67     if (m_page) {
68         // Store m_page in a local variable, clearing m_page, so that SVGImageChromeClient knows we're destructed.
69         std::unique_ptr<Page> currentPage = WTFMove(m_page);
70         currentPage->mainFrame().loader().frameDetached(); // Break both the loader and view references to the frame
71     }
72
73     // Verify that page teardown destroyed the Chrome
74     ASSERT(!m_chromeClient || !m_chromeClient->image());
75 }
76
77 inline SVGSVGElement* SVGImage::rootElement() const
78 {
79     if (!m_page)
80         return nullptr;
81     return downcast<SVGDocument>(*m_page->mainFrame().document()).rootElement();
82 }
83
84 bool SVGImage::hasSingleSecurityOrigin() const
85 {
86     SVGSVGElement* rootElement = this->rootElement();
87     if (!rootElement)
88         return true;
89
90     // FIXME: Once foreignObject elements within SVG images are updated to not leak cross-origin data
91     // (e.g., visited links, spellcheck) we can remove the SVGForeignObjectElement check here and
92     // research if we can remove the Image::hasSingleSecurityOrigin mechanism entirely.
93     for (auto& element : descendantsOfType<SVGElement>(*rootElement)) {
94         if (is<SVGForeignObjectElement>(element))
95             return false;
96         if (is<SVGImageElement>(element)) {
97             if (!downcast<SVGImageElement>(element).hasSingleSecurityOrigin())
98                 return false;
99         } else if (is<SVGFEImageElement>(element)) {
100             if (!downcast<SVGFEImageElement>(element).hasSingleSecurityOrigin())
101                 return false;
102         }
103     }
104
105     // Because SVG image rendering disallows external resources and links,
106     // these images effectively are restricted to a single security origin.
107     return true;
108 }
109
110 void SVGImage::setContainerSize(const FloatSize& size)
111 {
112     if (!usesContainerSize())
113         return;
114
115     SVGSVGElement* rootElement = this->rootElement();
116     if (!rootElement)
117         return;
118     auto* renderer = downcast<RenderSVGRoot>(rootElement->renderer());
119     if (!renderer)
120         return;
121
122     FrameView* view = frameView();
123     view->resize(this->containerSize());
124
125     renderer->setContainerSize(IntSize(size));
126 }
127
128 IntSize SVGImage::containerSize() const
129 {
130     SVGSVGElement* rootElement = this->rootElement();
131     if (!rootElement)
132         return IntSize();
133
134     auto* renderer = downcast<RenderSVGRoot>(rootElement->renderer());
135     if (!renderer)
136         return IntSize();
137
138     // If a container size is available it has precedence.
139     IntSize containerSize = renderer->containerSize();
140     if (!containerSize.isEmpty())
141         return containerSize;
142
143     // Assure that a container size is always given for a non-identity zoom level.
144     ASSERT(renderer->style().effectiveZoom() == 1);
145
146     FloatSize currentSize;
147     if (rootElement->hasIntrinsicWidth() && rootElement->hasIntrinsicHeight())
148         currentSize = rootElement->currentViewportSize();
149     else
150         currentSize = rootElement->currentViewBoxRect().size();
151
152     if (!currentSize.isEmpty())
153         return IntSize(static_cast<int>(ceilf(currentSize.width())), static_cast<int>(ceilf(currentSize.height())));
154
155     // As last resort, use CSS default intrinsic size.
156     return IntSize(300, 150);
157 }
158
159 void SVGImage::drawForContainer(GraphicsContext& context, const FloatSize containerSize, float zoom, const FloatRect& dstRect,
160     const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode)
161 {
162     if (!m_page)
163         return;
164
165     ImageObserver* observer = imageObserver();
166     ASSERT(observer);
167
168     // Temporarily reset image observer, we don't want to receive any changeInRect() calls due to this relayout.
169     setImageObserver(nullptr);
170
171     IntSize roundedContainerSize = roundedIntSize(containerSize);
172     setContainerSize(roundedContainerSize);
173
174     FloatRect scaledSrc = srcRect;
175     scaledSrc.scale(1 / zoom);
176
177     // Compensate for the container size rounding by adjusting the source rect.
178     FloatSize adjustedSrcSize = scaledSrc.size();
179     adjustedSrcSize.scale(roundedContainerSize.width() / containerSize.width(), roundedContainerSize.height() / containerSize.height());
180     scaledSrc.setSize(adjustedSrcSize);
181
182     draw(context, dstRect, scaledSrc, compositeOp, blendMode, ImageOrientationDescription());
183
184     setImageObserver(observer);
185 }
186
187 #if USE(CAIRO)
188 // Passes ownership of the native image to the caller so NativeImagePtr needs
189 // to be a smart pointer type.
190 NativeImagePtr SVGImage::nativeImageForCurrentFrame()
191 {
192     if (!m_page)
193         return nullptr;
194
195     // Cairo does not use the accelerated drawing flag, so it's OK to make an unconditionally unaccelerated buffer.
196     std::unique_ptr<ImageBuffer> buffer = ImageBuffer::create(size(), Unaccelerated);
197     if (!buffer) // failed to allocate image
198         return nullptr;
199
200     draw(buffer->context(), rect(), rect(), CompositeSourceOver, BlendModeNormal, ImageOrientationDescription());
201
202     // FIXME: WK(Bug 113657): We should use DontCopyBackingStore here.
203     return buffer->copyImage(CopyBackingStore)->nativeImageForCurrentFrame();
204 }
205 #endif
206
207 void SVGImage::drawPatternForContainer(GraphicsContext& context, const FloatSize& containerSize, float zoom, const FloatRect& srcRect,
208     const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator compositeOp, const FloatRect& dstRect, BlendMode blendMode)
209 {
210     FloatRect zoomedContainerRect = FloatRect(FloatPoint(), containerSize);
211     zoomedContainerRect.scale(zoom);
212
213     // The ImageBuffer size needs to be scaled to match the final resolution.
214     AffineTransform transform = context.getCTM();
215     FloatSize imageBufferScale = FloatSize(transform.xScale(), transform.yScale());
216     ASSERT(imageBufferScale.width());
217     ASSERT(imageBufferScale.height());
218
219     FloatRect imageBufferSize = zoomedContainerRect;
220     imageBufferSize.scale(imageBufferScale.width(), imageBufferScale.height());
221
222     std::unique_ptr<ImageBuffer> buffer = ImageBuffer::createCompatibleBuffer(expandedIntSize(imageBufferSize.size()), 1, ColorSpaceSRGB, context, true);
223     if (!buffer) // Failed to allocate buffer.
224         return;
225     drawForContainer(buffer->context(), containerSize, zoom, imageBufferSize, zoomedContainerRect, CompositeSourceOver, BlendModeNormal);
226     if (context.drawLuminanceMask())
227         buffer->convertToLuminanceMask();
228
229     RefPtr<Image> image = ImageBuffer::sinkIntoImage(WTFMove(buffer), Unscaled);
230     if (!image)
231         return;
232
233     // Adjust the source rect and transform due to the image buffer's scaling.
234     FloatRect scaledSrcRect = srcRect;
235     scaledSrcRect.scale(imageBufferScale.width(), imageBufferScale.height());
236     AffineTransform unscaledPatternTransform(patternTransform);
237     unscaledPatternTransform.scale(1 / imageBufferScale.width(), 1 / imageBufferScale.height());
238
239     context.setDrawLuminanceMask(false);
240     image->drawPattern(context, scaledSrcRect, unscaledPatternTransform, phase, spacing, compositeOp, dstRect, blendMode);
241 }
242
243 void SVGImage::draw(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription)
244 {
245     if (!m_page)
246         return;
247
248     FrameView* view = frameView();
249     ASSERT(view);
250
251     GraphicsContextStateSaver stateSaver(context);
252     context.setCompositeOperation(compositeOp, blendMode);
253     context.clip(enclosingIntRect(dstRect));
254
255     float alpha = context.alpha();
256     bool compositingRequiresTransparencyLayer = compositeOp != CompositeSourceOver || blendMode != BlendModeNormal || alpha < 1;
257     if (compositingRequiresTransparencyLayer) {
258         context.beginTransparencyLayer(alpha);
259         context.setCompositeOperation(CompositeSourceOver, BlendModeNormal);
260     }
261
262     FloatSize scale(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height());
263     
264     // We can only draw the entire frame, clipped to the rect we want. So compute where the top left
265     // of the image would be if we were drawing without clipping, and translate accordingly.
266     FloatSize topLeftOffset(srcRect.location().x() * scale.width(), srcRect.location().y() * scale.height());
267     FloatPoint destOffset = dstRect.location() - topLeftOffset;
268
269     context.translate(destOffset.x(), destOffset.y());
270     context.scale(scale);
271
272     view->resize(containerSize());
273
274     if (!m_url.isEmpty())
275         view->scrollToFragment(m_url);
276     
277     if (view->needsLayout())
278         view->layout();
279
280     view->paint(context, intersection(context.clipBounds(), enclosingIntRect(srcRect)));
281
282     if (compositingRequiresTransparencyLayer)
283         context.endTransparencyLayer();
284
285     stateSaver.restore();
286
287     if (imageObserver())
288         imageObserver()->didDraw(this);
289 }
290
291 RenderBox* SVGImage::embeddedContentBox() const
292 {
293     SVGSVGElement* rootElement = this->rootElement();
294     if (!rootElement)
295         return nullptr;
296     return downcast<RenderBox>(rootElement->renderer());
297 }
298
299 FrameView* SVGImage::frameView() const
300 {
301     if (!m_page)
302         return nullptr;
303     return m_page->mainFrame().view();
304 }
305
306 bool SVGImage::hasRelativeWidth() const
307 {
308     SVGSVGElement* rootElement = this->rootElement();
309     if (!rootElement)
310         return false;
311     return rootElement->intrinsicWidth().isPercentOrCalculated();
312 }
313
314 bool SVGImage::hasRelativeHeight() const
315 {
316     SVGSVGElement* rootElement = this->rootElement();
317     if (!rootElement)
318         return false;
319     return rootElement->intrinsicHeight().isPercentOrCalculated();
320 }
321
322 void SVGImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
323 {
324     SVGSVGElement* rootElement = this->rootElement();
325     if (!rootElement)
326         return;
327
328     intrinsicWidth = rootElement->intrinsicWidth();
329     intrinsicHeight = rootElement->intrinsicHeight();
330     if (rootElement->preserveAspectRatio().align() == SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE)
331         return;
332
333     intrinsicRatio = rootElement->viewBox().size();
334     if (intrinsicRatio.isEmpty() && intrinsicWidth.isFixed() && intrinsicHeight.isFixed())
335         intrinsicRatio = FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0));
336 }
337
338 // FIXME: support catchUpIfNecessary.
339 void SVGImage::startAnimation(CatchUpAnimation)
340 {
341     SVGSVGElement* rootElement = this->rootElement();
342     if (!rootElement)
343         return;
344     rootElement->unpauseAnimations();
345     rootElement->setCurrentTime(0);
346 }
347
348 void SVGImage::stopAnimation()
349 {
350     SVGSVGElement* rootElement = this->rootElement();
351     if (!rootElement)
352         return;
353     rootElement->pauseAnimations();
354 }
355
356 void SVGImage::resetAnimation()
357 {
358     stopAnimation();
359 }
360
361 void SVGImage::reportApproximateMemoryCost() const
362 {
363     Document* document = m_page->mainFrame().document();
364     size_t decodedImageMemoryCost = 0;
365
366     for (Node* node = document; node; node = NodeTraversal::next(*node))
367         decodedImageMemoryCost += node->approximateMemoryCost();
368
369     JSC::VM& vm = JSDOMWindowBase::commonVM();
370     JSC::JSLockHolder lock(vm);
371     // FIXME: Adopt reportExtraMemoryVisited, and switch to reportExtraMemoryAllocated.
372     // https://bugs.webkit.org/show_bug.cgi?id=142595
373     vm.heap.deprecatedReportExtraMemory(decodedImageMemoryCost + data()->size());
374 }
375
376 bool SVGImage::dataChanged(bool allDataReceived)
377 {
378     // Don't do anything if is an empty image.
379     if (!data()->size())
380         return true;
381
382     if (allDataReceived) {
383         PageConfiguration pageConfiguration(makeUniqueRef<EmptyEditorClient>(), makeUniqueRef<EmptySocketProvider>());
384         fillWithEmptyClients(pageConfiguration);
385         m_chromeClient = std::make_unique<SVGImageChromeClient>(this);
386         pageConfiguration.chromeClient = m_chromeClient.get();
387
388         // FIXME: If this SVG ends up loading itself, we might leak the world.
389         // The Cache code does not know about CachedImages holding Frames and
390         // won't know to break the cycle.
391         // This will become an issue when SVGImage will be able to load other
392         // SVGImage objects, but we're safe now, because SVGImage can only be
393         // loaded by a top-level document.
394         m_page = std::make_unique<Page>(WTFMove(pageConfiguration));
395         m_page->settings().setMediaEnabled(false);
396         m_page->settings().setScriptEnabled(false);
397         m_page->settings().setPluginsEnabled(false);
398
399         Frame& frame = m_page->mainFrame();
400         frame.setView(FrameView::create(frame));
401         frame.init();
402         FrameLoader& loader = frame.loader();
403         loader.forceSandboxFlags(SandboxAll);
404
405         frame.view()->setCanHaveScrollbars(false); // SVG Images will always synthesize a viewBox, if it's not available, and thus never see scrollbars.
406         frame.view()->setTransparent(true); // SVG Images are transparent.
407
408         ASSERT(loader.activeDocumentLoader()); // DocumentLoader should have been created by frame->init().
409         loader.activeDocumentLoader()->writer().setMIMEType("image/svg+xml");
410         loader.activeDocumentLoader()->writer().begin(URL()); // create the empty document
411         loader.activeDocumentLoader()->writer().addData(data()->data(), data()->size());
412         loader.activeDocumentLoader()->writer().end();
413
414         // Set the intrinsic size before a container size is available.
415         m_intrinsicSize = containerSize();
416         reportApproximateMemoryCost();
417     }
418
419     return m_page != nullptr;
420 }
421
422 String SVGImage::filenameExtension() const
423 {
424     return "svg";
425 }
426
427 bool isInSVGImage(const Element* element)
428 {
429     ASSERT(element);
430
431     Page* page = element->document().page();
432     if (!page)
433         return false;
434
435     return page->chrome().client().isSVGImageChromeClient();
436 }
437
438 void SVGImage::dump(TextStream& ts) const
439 {
440     Image::dump(ts);
441     ts.dumpProperty("url", m_url.string());
442 }
443
444
445 }