Reviewed by Sam.
[WebKit-https.git] / WebCore / svg / graphics / SVGImage.cpp
1 /*
2  * Copyright (C) 2006 Eric Seidel (eric@webkit.org)
3  * Copyright (C) 2008 Apple, Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #if ENABLE(SVG)
29 #include "SVGImage.h"
30
31 #include "CachedPage.h"
32 #include "DocumentLoader.h"
33 #include "FloatRect.h"
34 #include "Frame.h"
35 #include "FrameLoader.h"
36 #include "FrameView.h"
37 #include "GraphicsContext.h"
38 #include "ImageObserver.h"
39 #include "NotImplemented.h"
40 #include "Page.h"
41 #include "RenderView.h"
42 #include "ResourceError.h"
43 #include "SVGDocument.h"
44 #include "SVGLength.h"
45 #include "SVGRenderSupport.h"
46 #include "SVGSVGElement.h"
47 #include "Settings.h"
48
49 #include "SVGImageEmptyClients.h"
50
51 namespace WebCore {
52
53 SVGImage::SVGImage(ImageObserver* observer)
54     : Image(observer)
55     , m_document(0)
56     , m_page(0)
57     , m_frame(0)
58     , m_frameView(0)
59 {
60 }
61
62 SVGImage::~SVGImage()
63 {
64     if (m_frame)
65         m_frame->loader()->frameDetached(); // Break both the loader and view references to the frame
66 }
67
68 void SVGImage::setContainerSize(const IntSize& containerSize)
69 {
70     if (containerSize.width() <= 0 || containerSize.height() <= 0)
71         return;
72
73     if (!m_frame || !m_frame->document())
74         return;
75     SVGSVGElement* rootElement = static_cast<SVGDocument*>(m_frame->document())->rootElement();
76     if (!rootElement)
77         return;
78
79     rootElement->setContainerSize(containerSize);
80 }
81
82 bool SVGImage::usesContainerSize() const
83 {
84     if (!m_frame || !m_frame->document())
85         return false;
86     SVGSVGElement* rootElement = static_cast<SVGDocument*>(m_frame->document())->rootElement();
87     if (!rootElement)
88         return false;
89
90     return rootElement->hasSetContainerSize();
91 }
92
93 IntSize SVGImage::size() const
94 {
95     if (!m_frame || !m_frame->document())
96         return IntSize();
97     
98     SVGSVGElement* rootElement = static_cast<SVGDocument*>(m_frame->document())->rootElement();
99     if (!rootElement)
100         return IntSize();
101     
102     SVGLength width = rootElement->width();
103     SVGLength height = rootElement->height();
104     
105     IntSize svgSize;
106     if (width.unitType() == LengthTypePercentage) 
107         svgSize.setWidth(rootElement->relativeWidthValue());
108     else
109         svgSize.setWidth(static_cast<int>(width.value()));
110
111     if (height.unitType() == LengthTypePercentage) 
112         svgSize.setHeight(rootElement->relativeHeightValue());
113     else
114         svgSize.setHeight(static_cast<int>(height.value()));
115
116     return svgSize;
117 }
118
119 bool SVGImage::hasRelativeWidth() const
120 {
121     if (!m_frame || !m_frame->document())
122         return false;
123     SVGSVGElement* rootElement = static_cast<SVGDocument*>(m_frame->document())->rootElement();
124     if (!rootElement)
125         return false;
126
127     return rootElement->width().unitType() == LengthTypePercentage;
128 }
129
130 bool SVGImage::hasRelativeHeight() const
131 {
132     if (!m_frame || !m_frame->document())
133         return false;
134     SVGSVGElement* rootElement = static_cast<SVGDocument*>(m_frame->document())->rootElement();
135     if (!rootElement)
136         return false;
137
138     return rootElement->height().unitType() == LengthTypePercentage;
139 }
140
141 void SVGImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp)
142 {
143     if (!m_frame)
144         return;
145     
146     context->save();
147     context->setCompositeOperation(compositeOp);
148     context->clip(enclosingIntRect(dstRect));
149     if (compositeOp != CompositeSourceOver)
150         context->beginTransparencyLayer(1.0f);
151     context->translate(dstRect.location().x(), dstRect.location().y());
152     context->scale(FloatSize(dstRect.width()/srcRect.width(), dstRect.height()/srcRect.height()));
153     
154     if (m_frame->view()->needsLayout())
155         m_frame->view()->layout();
156     m_frame->paint(context, enclosingIntRect(srcRect));
157
158     if (compositeOp != CompositeSourceOver)
159         context->endTransparencyLayer();
160
161     context->restore();
162
163     if (imageObserver())
164         imageObserver()->didDraw(this);
165 }
166
167 NativeImagePtr SVGImage::nativeImageForCurrentFrame()
168 {
169     // FIXME: In order to support dynamic SVGs we need to have a way to invalidate this
170     // frame cache, or better yet, not use a cache for tiled drawing at all, instead
171     // having a tiled drawing callback (hopefully non-virtual).
172     if (!m_frameCache) {
173         m_frameCache.set(ImageBuffer::create(size(), false).release());
174         if (!m_frameCache) // failed to allocate image
175             return 0;
176         renderSubtreeToImage(m_frameCache.get(), m_frame->contentRenderer());
177     }
178 #if PLATFORM(CG)
179     return m_frameCache->cgImage();
180 #elif PLATFORM(QT)
181     return m_frameCache->pixmap();
182 #elif PLATFORM(CAIRO)
183     return m_frameCache->surface();
184 #else
185     notImplemented();
186     return 0;
187 #endif
188 }
189
190 bool SVGImage::dataChanged(bool allDataReceived)
191 {
192     int length = m_data->size();
193     if (!length) // if this was an empty image
194         return true;
195     
196     if (allDataReceived) {
197         static ChromeClient* dummyChromeClient = new SVGEmptyChromeClient;
198         static FrameLoaderClient* dummyFrameLoaderClient =  new SVGEmptyFrameLoaderClient;
199         static EditorClient* dummyEditorClient = new SVGEmptyEditorClient;
200         static ContextMenuClient* dummyContextMenuClient = new SVGEmptyContextMenuClient;
201         static DragClient* dummyDragClient = new SVGEmptyDragClient;
202         static InspectorClient* dummyInspectorClient = new SVGEmptyInspectorClient;
203
204         // FIXME: If this SVG ends up loading itself, we'll leak this Frame (and associated DOM & render trees).
205         // The Cache code does not know about CachedImages holding Frames and won't know to break the cycle.
206         m_page.set(new Page(dummyChromeClient, dummyContextMenuClient, dummyEditorClient, dummyDragClient, dummyInspectorClient));
207         m_page->settings()->setJavaScriptEnabled(false);
208
209         m_frame = Frame::create(m_page.get(), 0, dummyFrameLoaderClient);
210         m_frameView = new FrameView(m_frame.get());
211         m_frameView->deref(); // FIXME: FrameView starts with a refcount of 1
212         m_frame->setView(m_frameView.get());
213         m_frame->init();
214         ResourceRequest fakeRequest(KURL(""));
215         m_frame->loader()->load(fakeRequest); // Make sure the DocumentLoader is created
216         m_frame->loader()->cancelContentPolicyCheck(); // cancel any policy checks
217         m_frame->loader()->commitProvisionalLoad(0);
218         m_frame->loader()->setResponseMIMEType("image/svg+xml");
219         m_frame->loader()->begin(KURL()); // create the empty document
220         m_frame->loader()->write(m_data->data(), m_data->size());
221         m_frame->loader()->end();
222         m_frameView->setTransparent(true); // SVG Images are transparent.
223     }
224     return m_frameView;
225 }
226
227 }
228
229 #endif // ENABLE(SVG)