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