1/* 2 * Copyright (C) 2006 Eric Seidel <eric@webkit.org> 3 * Copyright (C) 2008, 2009 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 30#if ENABLE(SVG) 31#include "SVGImage.h" 32 33#include "DocumentLoader.h" 34#include "FrameView.h" 35#include "ImageBuffer.h" 36#include "ImageObserver.h" 37#include "IntRect.h" 38#include "RenderSVGRoot.h" 39#include "RenderStyle.h" 40#include "SVGDocument.h" 41#include "SVGImageChromeClient.h" 42#include "SVGSVGElement.h" 43#include "Settings.h" 44 45namespace WebCore { 46 47SVGImage::SVGImage(ImageObserver* observer) 48 : Image(observer) 49{ 50} 51 52SVGImage::~SVGImage() 53{ 54 if (m_page) { 55 // Store m_page in a local variable, clearing m_page, so that SVGImageChromeClient knows we're destructed. 56 OwnPtr<Page> currentPage = m_page.release(); 57 currentPage->mainFrame()->loader()->frameDetached(); // Break both the loader and view references to the frame 58 } 59 60 // Verify that page teardown destroyed the Chrome 61 ASSERT(!m_chromeClient || !m_chromeClient->image()); 62} 63 64void SVGImage::setContainerSize(const IntSize& size) 65{ 66 if (!m_page || !usesContainerSize()) 67 return; 68 69 Frame* frame = m_page->mainFrame(); 70 SVGSVGElement* rootElement = toSVGDocument(frame->document())->rootElement(); 71 if (!rootElement) 72 return; 73 RenderSVGRoot* renderer = toRenderSVGRoot(rootElement->renderer()); 74 if (!renderer) 75 return; 76 77 FrameView* view = frameView(); 78 view->resize(this->containerSize()); 79 80 renderer->setContainerSize(size); 81} 82 83IntSize SVGImage::containerSize() const 84{ 85 if (!m_page) 86 return IntSize(); 87 Frame* frame = m_page->mainFrame(); 88 SVGSVGElement* rootElement = toSVGDocument(frame->document())->rootElement(); 89 if (!rootElement) 90 return IntSize(); 91 92 RenderSVGRoot* renderer = toRenderSVGRoot(rootElement->renderer()); 93 if (!renderer) 94 return IntSize(); 95 96 // If a container size is available it has precedence. 97 IntSize containerSize = renderer->containerSize(); 98 if (!containerSize.isEmpty()) 99 return containerSize; 100 101 // Assure that a container size is always given for a non-identity zoom level. 102 ASSERT(renderer->style()->effectiveZoom() == 1); 103 104 FloatSize currentSize; 105 if (rootElement->intrinsicWidth().isFixed() && rootElement->intrinsicHeight().isFixed()) 106 currentSize = rootElement->currentViewportSize(); 107 else 108 currentSize = rootElement->currentViewBoxRect().size(); 109 110 if (!currentSize.isEmpty()) 111 return IntSize(static_cast<int>(ceilf(currentSize.width())), static_cast<int>(ceilf(currentSize.height()))); 112 113 // As last resort, use CSS default intrinsic size. 114 return IntSize(300, 150); 115} 116 117void SVGImage::drawForContainer(GraphicsContext* context, const FloatSize containerSize, float zoom, const FloatRect& dstRect, 118 const FloatRect& srcRect, ColorSpace colorSpace, CompositeOperator compositeOp, BlendMode blendMode) 119{ 120 if (!m_page) 121 return; 122 123 ImageObserver* observer = imageObserver(); 124 ASSERT(observer); 125 126 // Temporarily reset image observer, we don't want to receive any changeInRect() calls due to this relayout. 127 setImageObserver(0); 128 129 IntSize roundedContainerSize = roundedIntSize(containerSize); 130 setContainerSize(roundedContainerSize); 131 132 FloatRect scaledSrc = srcRect; 133 scaledSrc.scale(1 / zoom); 134 135 // Compensate for the container size rounding by adjusting the source rect. 136 FloatSize adjustedSrcSize = scaledSrc.size(); 137 adjustedSrcSize.scale(roundedContainerSize.width() / containerSize.width(), roundedContainerSize.height() / containerSize.height()); 138 scaledSrc.setSize(adjustedSrcSize); 139 140 draw(context, dstRect, scaledSrc, colorSpace, compositeOp, blendMode); 141 142 setImageObserver(observer); 143} 144 145#if USE(CAIRO) 146// Passes ownership of the native image to the caller so PassNativeImagePtr needs 147// to be a smart pointer type. 148PassNativeImagePtr SVGImage::nativeImageForCurrentFrame() 149{ 150 if (!m_page) 151 return 0; 152 153 OwnPtr<ImageBuffer> buffer = ImageBuffer::create(size(), 1); 154 if (!buffer) // failed to allocate image 155 return 0; 156 157 draw(buffer->context(), rect(), rect(), ColorSpaceDeviceRGB, CompositeSourceOver, BlendModeNormal); 158 159 // FIXME: WK(Bug 113657): We should use DontCopyBackingStore here. 160 return buffer->copyImage(CopyBackingStore)->nativeImageForCurrentFrame(); 161} 162#endif 163 164void SVGImage::drawPatternForContainer(GraphicsContext* context, const FloatSize containerSize, float zoom, const FloatRect& srcRect, 165 const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace colorSpace, CompositeOperator compositeOp, const FloatRect& dstRect) 166{ 167 FloatRect zoomedContainerRect = FloatRect(FloatPoint(), containerSize); 168 zoomedContainerRect.scale(zoom); 169 170 // The ImageBuffer size needs to be scaled to match the final resolution. 171 AffineTransform transform = context->getCTM(); 172 FloatSize imageBufferScale = FloatSize(transform.xScale(), transform.yScale()); 173 ASSERT(imageBufferScale.width()); 174 ASSERT(imageBufferScale.height()); 175 176 FloatRect imageBufferSize = zoomedContainerRect; 177 imageBufferSize.scale(imageBufferScale.width(), imageBufferScale.height()); 178 179 OwnPtr<ImageBuffer> buffer = ImageBuffer::create(expandedIntSize(imageBufferSize.size()), 1); 180 if (!buffer) // Failed to allocate buffer. 181 return; 182 drawForContainer(buffer->context(), containerSize, zoom, imageBufferSize, zoomedContainerRect, ColorSpaceDeviceRGB, CompositeSourceOver, BlendModeNormal); 183 RefPtr<Image> image = buffer->copyImage(DontCopyBackingStore, Unscaled); 184 185 // Adjust the source rect and transform due to the image buffer's scaling. 186 FloatRect scaledSrcRect = srcRect; 187 scaledSrcRect.scale(imageBufferScale.width(), imageBufferScale.height()); 188 AffineTransform unscaledPatternTransform(patternTransform); 189 unscaledPatternTransform.scale(1 / imageBufferScale.width(), 1 / imageBufferScale.height()); 190 191 image->drawPattern(context, scaledSrcRect, unscaledPatternTransform, phase, colorSpace, compositeOp, dstRect); 192} 193 194void SVGImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace, CompositeOperator compositeOp, BlendMode blendMode) 195{ 196 if (!m_page) 197 return; 198 199 FrameView* view = frameView(); 200 201 GraphicsContextStateSaver stateSaver(*context); 202 context->setCompositeOperation(compositeOp, blendMode); 203 context->clip(enclosingIntRect(dstRect)); 204 if (compositeOp != CompositeSourceOver) 205 context->beginTransparencyLayer(1); 206 207 FloatSize scale(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height()); 208 209 // We can only draw the entire frame, clipped to the rect we want. So compute where the top left 210 // of the image would be if we were drawing without clipping, and translate accordingly. 211 FloatSize topLeftOffset(srcRect.location().x() * scale.width(), srcRect.location().y() * scale.height()); 212 FloatPoint destOffset = dstRect.location() - topLeftOffset; 213 214 context->translate(destOffset.x(), destOffset.y()); 215 context->scale(scale); 216 217 view->resize(containerSize()); 218 219 if (view->needsLayout()) 220 view->layout(); 221 222 view->paint(context, enclosingIntRect(srcRect)); 223 224 if (compositeOp != CompositeSourceOver) 225 context->endTransparencyLayer(); 226 227 stateSaver.restore(); 228 229 if (imageObserver()) 230 imageObserver()->didDraw(this); 231} 232 233RenderBox* SVGImage::embeddedContentBox() const 234{ 235 if (!m_page) 236 return 0; 237 Frame* frame = m_page->mainFrame(); 238 SVGSVGElement* rootElement = toSVGDocument(frame->document())->rootElement(); 239 if (!rootElement) 240 return 0; 241 return toRenderBox(rootElement->renderer()); 242} 243 244FrameView* SVGImage::frameView() const 245{ 246 if (!m_page) 247 return 0; 248 249 return m_page->mainFrame()->view(); 250} 251 252bool SVGImage::hasRelativeWidth() const 253{ 254 if (!m_page) 255 return false; 256 Frame* frame = m_page->mainFrame(); 257 SVGSVGElement* rootElement = toSVGDocument(frame->document())->rootElement(); 258 if (!rootElement) 259 return false; 260 return rootElement->intrinsicWidth().isPercent(); 261} 262 263bool SVGImage::hasRelativeHeight() const 264{ 265 if (!m_page) 266 return false; 267 Frame* frame = m_page->mainFrame(); 268 SVGSVGElement* rootElement = toSVGDocument(frame->document())->rootElement(); 269 if (!rootElement) 270 return false; 271 return rootElement->intrinsicHeight().isPercent(); 272} 273 274void SVGImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) 275{ 276 if (!m_page) 277 return; 278 Frame* frame = m_page->mainFrame(); 279 SVGSVGElement* rootElement = toSVGDocument(frame->document())->rootElement(); 280 if (!rootElement) 281 return; 282 283 intrinsicWidth = rootElement->intrinsicWidth(); 284 intrinsicHeight = rootElement->intrinsicHeight(); 285 if (rootElement->preserveAspectRatio().align() == SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE) 286 return; 287 288 intrinsicRatio = rootElement->viewBox().size(); 289 if (intrinsicRatio.isEmpty() && intrinsicWidth.isFixed() && intrinsicHeight.isFixed()) 290 intrinsicRatio = FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0)); 291} 292 293// FIXME: support catchUpIfNecessary. 294void SVGImage::startAnimation(bool /* catchUpIfNecessary */) 295{ 296 if (!m_page) 297 return; 298 Frame* frame = m_page->mainFrame(); 299 SVGSVGElement* rootElement = toSVGDocument(frame->document())->rootElement(); 300 if (!rootElement) 301 return; 302 rootElement->unpauseAnimations(); 303 rootElement->setCurrentTime(0); 304} 305 306void SVGImage::stopAnimation() 307{ 308 if (!m_page) 309 return; 310 Frame* frame = m_page->mainFrame(); 311 SVGSVGElement* rootElement = toSVGDocument(frame->document())->rootElement(); 312 if (!rootElement) 313 return; 314 rootElement->pauseAnimations(); 315} 316 317void SVGImage::resetAnimation() 318{ 319 stopAnimation(); 320} 321 322bool SVGImage::dataChanged(bool allDataReceived) 323{ 324 // Don't do anything if is an empty image. 325 if (!data()->size()) 326 return true; 327 328 if (allDataReceived) { 329 static FrameLoaderClient* dummyFrameLoaderClient = new EmptyFrameLoaderClient; 330 331 Page::PageClients pageClients; 332 fillWithEmptyClients(pageClients); 333 m_chromeClient = adoptPtr(new SVGImageChromeClient(this)); 334 pageClients.chromeClient = m_chromeClient.get(); 335 336 // FIXME: If this SVG ends up loading itself, we might leak the world. 337 // The Cache code does not know about CachedImages holding Frames and 338 // won't know to break the cycle. 339 // This will become an issue when SVGImage will be able to load other 340 // SVGImage objects, but we're safe now, because SVGImage can only be 341 // loaded by a top-level document. 342 m_page = adoptPtr(new Page(pageClients)); 343 m_page->settings()->setMediaEnabled(false); 344 m_page->settings()->setScriptEnabled(false); 345 m_page->settings()->setPluginsEnabled(false); 346 347 RefPtr<Frame> frame = Frame::create(m_page.get(), 0, dummyFrameLoaderClient); 348 frame->setView(FrameView::create(frame.get())); 349 frame->init(); 350 FrameLoader* loader = frame->loader(); 351 loader->forceSandboxFlags(SandboxAll); 352 353 frame->view()->setCanHaveScrollbars(false); // SVG Images will always synthesize a viewBox, if it's not available, and thus never see scrollbars. 354 frame->view()->setTransparent(true); // SVG Images are transparent. 355 356 ASSERT(loader->activeDocumentLoader()); // DocumentLoader should have been created by frame->init(). 357 loader->activeDocumentLoader()->writer()->setMIMEType("image/svg+xml"); 358 loader->activeDocumentLoader()->writer()->begin(KURL()); // create the empty document 359 loader->activeDocumentLoader()->writer()->addData(data()->data(), data()->size()); 360 loader->activeDocumentLoader()->writer()->end(); 361 362 // Set the intrinsic size before a container size is available. 363 m_intrinsicSize = containerSize(); 364 } 365 366 return m_page; 367} 368 369String SVGImage::filenameExtension() const 370{ 371 return "svg"; 372} 373 374} 375 376#endif // ENABLE(SVG) 377