2007-01-17 Eric Seidel <eric@webkit.org>
[WebKit-https.git] / WebCore / platform / graphics / svg / SVGImage.cpp
1 /*
2  * Copyright (C) 2006 Eric Seidel (eric@webkit.org)
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #ifdef SVG_SUPPORT
28
29 #include "DocumentLoader.h"
30 #include "FloatRect.h"
31 #include "FrameLoader.h"
32 #include "FrameMac.h"
33 #include "FrameView.h"
34 #include "GraphicsContext.h"
35 #include "Page.h"
36 #include "PageCache.h"
37 #include "ResourceError.h"
38 #include "SVGDocument.h"
39 #include "SVGImage.h"
40 #include "SVGLength.h"
41 #include "SVGSVGElement.h"
42 #include "Settings.h"
43
44 #include "SVGImageEmptyClients.h"
45
46 namespace WebCore {
47
48 SVGImage::SVGImage(ImageAnimationObserver* observer)
49     : Image(observer)
50     , m_document(0)
51     , m_page(0)
52     , m_frame(0)
53     , m_frameView(0)
54 {
55 }
56
57 SVGImage::~SVGImage()
58 {
59     if (m_frame)
60         m_frame->loader()->frameDetached(); // Break both the loader and view references to the frame
61 }
62
63 IntSize SVGImage::size() const
64 {
65     IntSize defaultSize(300, 150);
66     // FIXME: Eventually we'll be passed in the dest size and can scale against that
67     IntSize destSize = defaultSize;
68     
69     if (!m_frame || !m_frame->document())
70         return IntSize();
71     
72     SVGSVGElement* rootElement = static_cast<SVGDocument*>(m_frame->document())->rootElement();
73     if (!rootElement)
74         return defaultSize;
75     
76     SVGLength width = rootElement->width();
77     SVGLength height = rootElement->height();
78     
79     IntSize svgSize;
80     if (width.unitType() == LengthTypePercentage)
81         svgSize.setWidth(static_cast<int>(width.valueInSpecifiedUnits() * destSize.width()));
82     else
83         svgSize.setWidth(static_cast<int>(width.value()));
84     if (height.unitType() == LengthTypePercentage)
85         svgSize.setHeight(static_cast<int>(height.valueInSpecifiedUnits() * destSize.height()));
86     else
87         svgSize.setHeight(static_cast<int>(height.value()));
88     
89     return svgSize;
90 }
91
92 void SVGImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp)
93 {
94     if (!m_frame)
95         return;
96     
97     context->save();
98     context->clip(enclosingIntRect(dstRect));
99     context->translate(dstRect.location().x(), dstRect.location().y());
100     context->scale(FloatSize(dstRect.width()/srcRect.width(), dstRect.height()/srcRect.height()));
101     m_frame->paint(context, enclosingIntRect(srcRect));
102     context->restore();
103 }
104
105 NativeImagePtr SVGImage::nativeImageForCurrentFrame()
106 {
107     // FIXME: In order to support dynamic SVGs we need to have a way to invalidate this
108     // frame cache, or better yet, not use a cache for tiled drawing at all, instead
109     // having a tiled drawing callback (hopefully non-virtual).
110     if (!m_frameCache) {
111         m_frameCache.set(ImageBuffer::create(size(), false).release());
112         ImageBuffer::renderSubtreeToImage(m_frameCache.get(), m_frame->renderer());
113     }
114     return m_frameCache->cgImage();
115 }
116
117 bool SVGImage::setData(bool allDataReceived)
118 {
119     int length = dataBuffer().size();
120     if (!length) // if this was an empty image
121         return true;
122     
123     if (allDataReceived) {
124         static ChromeClient* dummyChromeClient = new SVGEmptyChromeClient;
125         static FrameLoaderClient* dummyFrameLoaderClient =  new SVGEmptyFrameLoaderClient;
126         static EditorClient* dummyEditorClient = new SVGEmptyEditorClient;
127         static ContextMenuClient* dummyContextMenuClient = new SVGEmptyContextMenuClient;
128         static Settings* dummySettings = new Settings;
129
130         // FIXME: If this SVG ends up loading itself, we'll leak this Frame (and associated DOM & render trees).
131         // The Cache code does not know about CachedImages holding Frames and won't know to break the cycle.
132         m_page.set(new Page(dummyChromeClient, dummyContextMenuClient, dummyEditorClient));
133         m_frame = new FrameMac(m_page.get(), 0, dummyFrameLoaderClient);
134         m_frameView = new FrameView(m_frame.get());
135         m_frameView->deref(); // FIXME: FrameView starts with a refcount of 1
136         m_frame->setView(m_frameView.get());
137         m_frame->setSettings(dummySettings);
138         ResourceRequest fakeRequest(KURL(""));
139         m_frame->loader()->load(fakeRequest); // Make sure the DocumentLoader is created
140         m_frame->loader()->cancelContentPolicyCheck(); // cancel any policy checks
141         m_frame->loader()->commitProvisionalLoad(0);
142         m_frame->loader()->setResponseMIMEType("image/svg+xml");
143         m_frame->loader()->begin("placeholder.svg"); // create the empty document
144         m_frame->loader()->write(dataBuffer().data(), dataBuffer().size());
145         m_frame->loader()->end();
146     }
147     return m_frameView;
148 }
149
150 }
151
152 #endif // SVG_SUPPORT