1 /* 2 * Copyright (C) 2006 Eric Seidel <eric (at) 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 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 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" // NOLINT 55 56 namespace WebCore { 57 58 class SVGImageChromeClient : public EmptyChromeClient { 59 WTF_MAKE_NONCOPYABLE(SVGImageChromeClient); WTF_MAKE_FAST_ALLOCATED; 60 public: 61 SVGImageChromeClient(SVGImage* image) 62 : m_image(image) 63 { 64 } 65 66 SVGImage* image() const { return m_image; } 67 68 private: 69 virtual void chromeDestroyed() 70 { 71 m_image = 0; 72 } 73 74 virtual void invalidateContentsAndWindow(const IntRect& r, bool) 75 { 76 if (m_image && m_image->imageObserver()) 77 m_image->imageObserver()->changedInRect(m_image, r); 78 } 79 80 SVGImage* m_image; 81 }; 82 83 SVGImage::SVGImage(ImageObserver* observer) 84 : Image(observer) 85 { 86 } 87 88 SVGImage::~SVGImage() 89 { 90 if (m_page) { 91 m_page->mainFrame()->loader()->frameDetached(); // Break both the loader and view references to the frame 92 93 // Clear explicitly because we want to delete the page before the ChromeClient. 94 // FIXME: I believe that's already guaranteed by C++ object destruction rules, 95 // so this may matter only for the assertion below. 96 m_page.clear(); 97 } 98 99 // Verify that page teardown destroyed the Chrome 100 ASSERT(!m_chromeClient || !m_chromeClient->image()); 101 } 102 103 void SVGImage::setContainerSize(const IntSize& containerSize) 104 { 105 if (containerSize.isEmpty()) 106 return; 107 108 if (!m_page) 109 return; 110 Frame* frame = m_page->mainFrame(); 111 SVGSVGElement* rootElement = static_cast<SVGDocument*>(frame->document())->rootElement(); 112 if (!rootElement) 113 return; 114 115 rootElement->setContainerSize(containerSize); 116 } 117 118 bool SVGImage::usesContainerSize() const 119 { 120 if (!m_page) 121 return false; 122 Frame* frame = m_page->mainFrame(); 123 SVGSVGElement* rootElement = static_cast<SVGDocument*>(frame->document())->rootElement(); 124 if (!rootElement) 125 return false; 126 127 return rootElement->hasSetContainerSize(); 128 } 129 130 IntSize SVGImage::size() const 131 { 132 if (!m_page) 133 return IntSize(); 134 Frame* frame = m_page->mainFrame(); 135 SVGSVGElement* rootElement = static_cast<SVGDocument*>(frame->document())->rootElement(); 136 if (!rootElement) 137 return IntSize(); 138 139 SVGLength width = rootElement->width(); 140 SVGLength height = rootElement->height(); 141 142 IntSize svgSize; 143 if (width.unitType() == LengthTypePercentage) 144 svgSize.setWidth(rootElement->relativeWidthValue()); 145 else 146 svgSize.setWidth(static_cast<int>(width.value(rootElement))); 147 148 if (height.unitType() == LengthTypePercentage) 149 svgSize.setHeight(rootElement->relativeHeightValue()); 150 else 151 svgSize.setHeight(static_cast<int>(height.value(rootElement))); 152 153 return svgSize; 154 } 155 156 bool SVGImage::hasRelativeWidth() const 157 { 158 if (!m_page) 159 return false; 160 SVGSVGElement* rootElement = static_cast<SVGDocument*>(m_page->mainFrame()->document())->rootElement(); 161 if (!rootElement) 162 return false; 163 164 return rootElement->width().unitType() == LengthTypePercentage; 165 } 166 167 bool SVGImage::hasRelativeHeight() const 168 { 169 if (!m_page) 170 return false; 171 SVGSVGElement* rootElement = static_cast<SVGDocument*>(m_page->mainFrame()->document())->rootElement(); 172 if (!rootElement) 173 return false; 174 175 return rootElement->height().unitType() == LengthTypePercentage; 176 } 177 178 void SVGImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace, CompositeOperator compositeOp) 179 { 180 if (!m_page) 181 return; 182 183 FrameView* view = m_page->mainFrame()->view(); 184 185 context->save(); 186 context->setCompositeOperation(compositeOp); 187 context->clip(enclosingIntRect(dstRect)); 188 if (compositeOp != CompositeSourceOver) 189 context->beginTransparencyLayer(1); 190 191 FloatSize scale(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height()); 192 193 // We can only draw the entire frame, clipped to the rect we want. So compute where the top left 194 // of the image would be if we were drawing without clipping, and translate accordingly. 195 FloatSize topLeftOffset(srcRect.location().x() * scale.width(), srcRect.location().y() * scale.height()); 196 FloatPoint destOffset = dstRect.location() - topLeftOffset; 197 198 context->translate(destOffset.x(), destOffset.y()); 199 context->scale(scale); 200 201 view->resize(size()); 202 203 if (view->needsLayout()) 204 view->layout(); 205 206 view->paint(context, IntRect(0, 0, view->width(), view->height())); 207 208 if (compositeOp != CompositeSourceOver) 209 context->endTransparencyLayer(); 210 211 context->restore(); 212 213 if (imageObserver()) 214 imageObserver()->didDraw(this); 215 } 216 217 NativeImagePtr SVGImage::nativeImageForCurrentFrame() 218 { 219 // FIXME: In order to support dynamic SVGs we need to have a way to invalidate this 220 // frame cache, or better yet, not use a cache for tiled drawing at all, instead 221 // having a tiled drawing callback (hopefully non-virtual). 222 if (!m_frameCache) { 223 if (!m_page) 224 return 0; 225 OwnPtr<ImageBuffer> buffer = ImageBuffer::create(size()); 226 if (!buffer) // failed to allocate image 227 return 0; 228 draw(buffer->context(), rect(), rect(), ColorSpaceDeviceRGB, CompositeSourceOver); 229 m_frameCache = buffer->copyImage(); 230 } 231 return m_frameCache->nativeImageForCurrentFrame(); 232 } 233 234 bool SVGImage::dataChanged(bool allDataReceived) 235 { 236 // Don't do anything if is an empty image. 237 if (!data()->size()) 238 return true; 239 240 if (allDataReceived) { 241 static FrameLoaderClient* dummyFrameLoaderClient = new EmptyFrameLoaderClient; 242 243 Page::PageClients pageClients; 244 m_chromeClient = adoptPtr(new SVGImageChromeClient(this)); 245 pageClients.chromeClient = m_chromeClient.get(); 246 #if ENABLE(CONTEXT_MENUS) 247 static ContextMenuClient* dummyContextMenuClient = new EmptyContextMenuClient; 248 pageClients.contextMenuClient = dummyContextMenuClient; 249 #endif 250 static EditorClient* dummyEditorClient = new EmptyEditorClient; 251 pageClients.editorClient = dummyEditorClient; 252 #if ENABLE(DRAG_SUPPORT) 253 static DragClient* dummyDragClient = new EmptyDragClient; 254 pageClients.dragClient = dummyDragClient; 255 #endif 256 static InspectorClient* dummyInspectorClient = new EmptyInspectorClient; 257 pageClients.inspectorClient = dummyInspectorClient; 258 #if ENABLE(DEVICE_ORIENTATION) 259 static DeviceMotionClient* dummyDeviceMotionClient = new EmptyDeviceMotionClient; 260 pageClients.deviceMotionClient = dummyDeviceMotionClient; 261 static DeviceOrientationClient* dummyDeviceOrientationClient = new EmptyDeviceOrientationClient; 262 pageClients.deviceOrientationClient = dummyDeviceOrientationClient; 263 #endif 264 265 // FIXME: If this SVG ends up loading itself, we might leak the world. 266 // The Cache code does not know about CachedImages holding Frames and 267 // won't know to break the cycle. 268 // This will become an issue when SVGImage will be able to load other 269 // SVGImage objects, but we're safe now, because SVGImage can only be 270 // loaded by a top-level document. 271 m_page.set(new Page(pageClients)); 272 m_page->settings()->setMediaEnabled(false); 273 m_page->settings()->setJavaScriptEnabled(false); 274 m_page->settings()->setPluginsEnabled(false); 275 276 RefPtr<Frame> frame = Frame::create(m_page.get(), 0, dummyFrameLoaderClient); 277 frame->setView(FrameView::create(frame.get())); 278 frame->init(); 279 FrameLoader* loader = frame->loader(); 280 loader->setForcedSandboxFlags(SandboxAll); 281 ASSERT(loader->activeDocumentLoader()); // DocumentLoader should have been created by frame->init(). 282 loader->activeDocumentLoader()->writer()->setMIMEType("image/svg+xml"); 283 loader->activeDocumentLoader()->writer()->begin(KURL()); // create the empty document 284 loader->activeDocumentLoader()->writer()->addData(data()->data(), data()->size()); 285 loader->activeDocumentLoader()->writer()->end(); 286 frame->view()->setTransparent(true); // SVG Images are transparent. 287 } 288 289 return m_page; 290 } 291 292 String SVGImage::filenameExtension() const 293 { 294 return "svg"; 295 } 296 297 } 298 299 #endif // ENABLE(SVG) 300