1/* 2 * Copyright (C) 2006 Eric Seidel (eric@webkit.org) 3 * Copyright (C) 2008, 2009 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 "FileChooser.h" 34#include "FloatRect.h" 35#include "Frame.h" 36#include "FrameLoader.h" 37#include "FrameView.h" 38#include "GraphicsContext.h" 39#include "HTMLFormElement.h" 40#include "ImageBuffer.h" 41#include "ImageObserver.h" 42#include "Page.h" 43#include "RenderView.h" 44#include "ResourceError.h" 45#include "SVGDocument.h" 46#include "SVGLength.h" 47#include "SVGRenderSupport.h" 48#include "SVGSVGElement.h" 49#include "Settings.h" 50 51// Moving this #include above FrameLoader.h causes the Windows build to fail due to warnings about 52// alignment in Timer<FrameLoader>. It seems that the definition of EmptyFrameLoaderClient is what 53// causes this (removing that definition fixes the warnings), but it isn't clear why. 54#include "EmptyClients.h" 55 56namespace WebCore { 57 58class SVGImageChromeClient : public EmptyChromeClient, public Noncopyable { 59public: 60 SVGImageChromeClient(SVGImage* image) 61 : m_image(image) 62 { 63 } 64 65 SVGImage* image() const { return m_image; } 66 67private: 68 virtual void chromeDestroyed() 69 { 70 m_image = 0; 71 } 72 73 virtual void repaint(const IntRect& r, bool, bool, bool) 74 { 75 if (m_image && m_image->imageObserver()) 76 m_image->imageObserver()->changedInRect(m_image, r); 77 } 78 79 SVGImage* m_image; 80}; 81 82SVGImage::SVGImage(ImageObserver* observer) 83 : Image(observer) 84{ 85} 86 87SVGImage::~SVGImage() 88{ 89 if (m_page) { 90 m_page->mainFrame()->loader()->frameDetached(); // Break both the loader and view references to the frame 91 92 // Clear explicitly because we want to delete the page before the ChromeClient. 93 // FIXME: I believe that's already guaranteed by C++ object destruction rules, 94 // so this may matter only for the assertion below. 95 m_page.clear(); 96 } 97 98 // Verify that page teardown destroyed the Chrome 99 ASSERT(!m_chromeClient || !m_chromeClient->image()); 100} 101 102void SVGImage::setContainerSize(const IntSize& containerSize) 103{ 104 if (containerSize.isEmpty()) 105 return; 106 107 if (!m_page) 108 return; 109 Frame* frame = m_page->mainFrame(); 110 SVGSVGElement* rootElement = static_cast<SVGDocument*>(frame->document())->rootElement(); 111 if (!rootElement) 112 return; 113 114 rootElement->setContainerSize(containerSize); 115} 116 117bool SVGImage::usesContainerSize() const 118{ 119 if (!m_page) 120 return false; 121 Frame* frame = m_page->mainFrame(); 122 SVGSVGElement* rootElement = static_cast<SVGDocument*>(frame->document())->rootElement(); 123 if (!rootElement) 124 return false; 125 126 return rootElement->hasSetContainerSize(); 127} 128 129IntSize SVGImage::size() const 130{ 131 if (!m_page) 132 return IntSize(); 133 Frame* frame = m_page->mainFrame(); 134 SVGSVGElement* rootElement = static_cast<SVGDocument*>(frame->document())->rootElement(); 135 if (!rootElement) 136 return IntSize(); 137 138 SVGLength width = rootElement->width(); 139 SVGLength height = rootElement->height(); 140 141 IntSize svgSize; 142 if (width.unitType() == LengthTypePercentage) 143 svgSize.setWidth(rootElement->relativeWidthValue()); 144 else 145 svgSize.setWidth(static_cast<int>(width.value(rootElement))); 146 147 if (height.unitType() == LengthTypePercentage) 148 svgSize.setHeight(rootElement->relativeHeightValue()); 149 else 150 svgSize.setHeight(static_cast<int>(height.value(rootElement))); 151 152 return svgSize; 153} 154 155bool SVGImage::hasRelativeWidth() const 156{ 157 if (!m_page) 158 return false; 159 SVGSVGElement* rootElement = static_cast<SVGDocument*>(m_page->mainFrame()->document())->rootElement(); 160 if (!rootElement) 161 return false; 162 163 return rootElement->width().unitType() == LengthTypePercentage; 164} 165 166bool SVGImage::hasRelativeHeight() const 167{ 168 if (!m_page) 169 return false; 170 SVGSVGElement* rootElement = static_cast<SVGDocument*>(m_page->mainFrame()->document())->rootElement(); 171 if (!rootElement) 172 return false; 173 174 return rootElement->height().unitType() == LengthTypePercentage; 175} 176 177void SVGImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace, CompositeOperator compositeOp) 178{ 179 if (!m_page) 180 return; 181 182 FrameView* view = m_page->mainFrame()->view(); 183 184 context->save(); 185 context->setCompositeOperation(compositeOp); 186 context->clip(enclosingIntRect(dstRect)); 187 if (compositeOp != CompositeSourceOver) 188 context->beginTransparencyLayer(1); 189 context->translate(dstRect.location().x(), dstRect.location().y()); 190 context->scale(FloatSize(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height())); 191 192 view->resize(size()); 193 194 if (view->needsLayout()) 195 view->layout(); 196 view->paint(context, IntRect(0, 0, view->width(), view->height())); 197 198 if (compositeOp != CompositeSourceOver) 199 context->endTransparencyLayer(); 200 201 context->restore(); 202 203 if (imageObserver()) 204 imageObserver()->didDraw(this); 205} 206 207NativeImagePtr SVGImage::nativeImageForCurrentFrame() 208{ 209 // FIXME: In order to support dynamic SVGs we need to have a way to invalidate this 210 // frame cache, or better yet, not use a cache for tiled drawing at all, instead 211 // having a tiled drawing callback (hopefully non-virtual). 212 if (!m_frameCache) { 213 if (!m_page) 214 return 0; 215 m_frameCache = ImageBuffer::create(size()); 216 if (!m_frameCache) // failed to allocate image 217 return 0; 218 renderSubtreeToImage(m_frameCache.get(), m_page->mainFrame()->contentRenderer()); 219 } 220 return m_frameCache->image()->nativeImageForCurrentFrame(); 221} 222 223bool SVGImage::dataChanged(bool allDataReceived) 224{ 225 // Don't do anything if is an empty image. 226 if (!data()->size()) 227 return true; 228 229 if (allDataReceived) { 230 static FrameLoaderClient* dummyFrameLoaderClient = new EmptyFrameLoaderClient; 231 static EditorClient* dummyEditorClient = new EmptyEditorClient; 232#if ENABLE(CONTEXT_MENUS) 233 static ContextMenuClient* dummyContextMenuClient = new EmptyContextMenuClient; 234#else 235 static ContextMenuClient* dummyContextMenuClient = 0; 236#endif 237#if ENABLE(DRAG_SUPPORT) 238 static DragClient* dummyDragClient = new EmptyDragClient; 239#else 240 static DragClient* dummyDragClient = 0; 241#endif 242 static InspectorClient* dummyInspectorClient = new EmptyInspectorClient; 243 244 m_chromeClient.set(new SVGImageChromeClient(this)); 245 246 // FIXME: If this SVG ends up loading itself, we might leak the world. 247 // The comment said that the Cache code does not know about CachedImages 248 // holding Frames and won't know to break the cycle. But 249 m_page.set(new Page(m_chromeClient.get(), dummyContextMenuClient, dummyEditorClient, dummyDragClient, dummyInspectorClient, 0, 0)); 250 m_page->settings()->setJavaScriptEnabled(false); 251 m_page->settings()->setPluginsEnabled(false); 252 253 RefPtr<Frame> frame = Frame::create(m_page.get(), 0, dummyFrameLoaderClient); 254 frame->setView(FrameView::create(frame.get())); 255 frame->init(); 256 ResourceRequest fakeRequest(KURL(ParsedURLString, "")); 257 FrameLoader* loader = frame->loader(); 258 loader->load(fakeRequest, false); // Make sure the DocumentLoader is created 259 loader->policyChecker()->cancelCheck(); // cancel any policy checks 260 loader->commitProvisionalLoad(0); 261 loader->setResponseMIMEType("image/svg+xml"); 262 loader->begin(KURL()); // create the empty document 263 loader->write(data()->data(), data()->size()); 264 loader->end(); 265 frame->view()->setTransparent(true); // SVG Images are transparent. 266 } 267 268 return m_page; 269} 270 271String SVGImage::filenameExtension() const 272{ 273 return "svg"; 274} 275 276} 277 278#endif // ENABLE(SVG) 279