diff options
Diffstat (limited to 'Source/WebCore/svg/graphics/SVGImage.cpp')
-rw-r--r-- | Source/WebCore/svg/graphics/SVGImage.cpp | 251 |
1 files changed, 165 insertions, 86 deletions
diff --git a/Source/WebCore/svg/graphics/SVGImage.cpp b/Source/WebCore/svg/graphics/SVGImage.cpp index 8ebf0444e..27cec3db7 100644 --- a/Source/WebCore/svg/graphics/SVGImage.cpp +++ b/Source/WebCore/svg/graphics/SVGImage.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Eric Seidel <eric@webkit.org> - * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2008-2009, 2015-2016 Apple Inc. All rights reserved. * Copyright (C) Research In Motion Limited 2011. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,30 +26,48 @@ */ #include "config.h" - -#if ENABLE(SVG) #include "SVGImage.h" #include "Chrome.h" +#include "CommonVM.h" +#include "DOMWindow.h" #include "DocumentLoader.h" +#include "EditorClient.h" #include "ElementIterator.h" +#include "FrameLoader.h" #include "FrameView.h" #include "ImageBuffer.h" #include "ImageObserver.h" #include "IntRect.h" +#include "JSDOMWindowBase.h" +#include "LibWebRTCProvider.h" #include "MainFrame.h" +#include "Page.h" +#include "PageConfiguration.h" #include "RenderSVGRoot.h" #include "RenderStyle.h" #include "SVGDocument.h" +#include "SVGFEImageElement.h" #include "SVGForeignObjectElement.h" -#include "SVGImageChromeClient.h" +#include "SVGImageClients.h" +#include "SVGImageElement.h" #include "SVGSVGElement.h" #include "Settings.h" +#include "SocketProvider.h" +#include "TextStream.h" +#include <runtime/JSCInlines.h> +#include <runtime/JSLock.h> + +#if USE(DIRECT2D) +#include "COMPtr.h" +#include <d2d1.h> +#endif namespace WebCore { -SVGImage::SVGImage(ImageObserver* observer) - : Image(observer) +SVGImage::SVGImage(ImageObserver& observer, const URL& url) + : Image(&observer) + , m_url(url) { } @@ -57,7 +75,7 @@ SVGImage::~SVGImage() { if (m_page) { // Store m_page in a local variable, clearing m_page, so that SVGImageChromeClient knows we're destructed. - std::unique_ptr<Page> currentPage = std::move(m_page); + std::unique_ptr<Page> currentPage = WTFMove(m_page); currentPage->mainFrame().loader().frameDetached(); // Break both the loader and view references to the frame } @@ -65,51 +83,64 @@ SVGImage::~SVGImage() ASSERT(!m_chromeClient || !m_chromeClient->image()); } -bool SVGImage::hasSingleSecurityOrigin() const +inline SVGSVGElement* SVGImage::rootElement() const { if (!m_page) - return true; + return nullptr; + return SVGDocument::rootElement(*m_page->mainFrame().document()); +} - SVGSVGElement* rootElement = toSVGDocument(m_page->mainFrame().document())->rootElement(); +bool SVGImage::hasSingleSecurityOrigin() const +{ + SVGSVGElement* rootElement = this->rootElement(); if (!rootElement) return true; - // Don't allow foreignObject elements since they can leak information with arbitrary HTML (like spellcheck or control theme). - if (descendantsOfType<SVGForeignObjectElement>(*rootElement).first()) - return false; + // FIXME: Once foreignObject elements within SVG images are updated to not leak cross-origin data + // (e.g., visited links, spellcheck) we can remove the SVGForeignObjectElement check here and + // research if we can remove the Image::hasSingleSecurityOrigin mechanism entirely. + for (auto& element : descendantsOfType<SVGElement>(*rootElement)) { + if (is<SVGForeignObjectElement>(element)) + return false; + if (is<SVGImageElement>(element)) { + if (!downcast<SVGImageElement>(element).hasSingleSecurityOrigin()) + return false; + } else if (is<SVGFEImageElement>(element)) { + if (!downcast<SVGFEImageElement>(element).hasSingleSecurityOrigin()) + return false; + } + } // Because SVG image rendering disallows external resources and links, // these images effectively are restricted to a single security origin. return true; } -void SVGImage::setContainerSize(const IntSize& size) +void SVGImage::setContainerSize(const FloatSize& size) { - if (!m_page || !usesContainerSize()) + if (!usesContainerSize()) return; - SVGSVGElement* rootElement = toSVGDocument(m_page->mainFrame().document())->rootElement(); + SVGSVGElement* rootElement = this->rootElement(); if (!rootElement) return; - RenderSVGRoot* renderer = toRenderSVGRoot(rootElement->renderer()); + auto* renderer = downcast<RenderSVGRoot>(rootElement->renderer()); if (!renderer) return; FrameView* view = frameView(); view->resize(this->containerSize()); - renderer->setContainerSize(size); + renderer->setContainerSize(IntSize(size)); } IntSize SVGImage::containerSize() const { - if (!m_page) - return IntSize(); - SVGSVGElement* rootElement = toSVGDocument(m_page->mainFrame().document())->rootElement(); + SVGSVGElement* rootElement = this->rootElement(); if (!rootElement) return IntSize(); - RenderSVGRoot* renderer = toRenderSVGRoot(rootElement->renderer()); + auto* renderer = downcast<RenderSVGRoot>(rootElement->renderer()); if (!renderer) return IntSize(); @@ -122,7 +153,7 @@ IntSize SVGImage::containerSize() const ASSERT(renderer->style().effectiveZoom() == 1); FloatSize currentSize; - if (rootElement->intrinsicWidth().isFixed() && rootElement->intrinsicHeight().isFixed()) + if (rootElement->hasIntrinsicWidth() && rootElement->hasIntrinsicHeight()) currentSize = rootElement->currentViewportSize(); else currentSize = rootElement->currentViewBoxRect().size(); @@ -134,8 +165,8 @@ IntSize SVGImage::containerSize() const return IntSize(300, 150); } -void SVGImage::drawForContainer(GraphicsContext* context, const FloatSize containerSize, float zoom, const FloatRect& dstRect, - const FloatRect& srcRect, ColorSpace colorSpace, CompositeOperator compositeOp, BlendMode blendMode) +void SVGImage::drawForContainer(GraphicsContext& context, const FloatSize containerSize, float zoom, const FloatRect& dstRect, + const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode) { if (!m_page) return; @@ -144,7 +175,7 @@ void SVGImage::drawForContainer(GraphicsContext* context, const FloatSize contai ASSERT(observer); // Temporarily reset image observer, we don't want to receive any changeInRect() calls due to this relayout. - setImageObserver(0); + setImageObserver(nullptr); IntSize roundedContainerSize = roundedIntSize(containerSize); setContainerSize(roundedContainerSize); @@ -157,38 +188,66 @@ void SVGImage::drawForContainer(GraphicsContext* context, const FloatSize contai adjustedSrcSize.scale(roundedContainerSize.width() / containerSize.width(), roundedContainerSize.height() / containerSize.height()); scaledSrc.setSize(adjustedSrcSize); - draw(context, dstRect, scaledSrc, colorSpace, compositeOp, blendMode, ImageOrientationDescription()); + draw(context, dstRect, scaledSrc, compositeOp, blendMode, ImageOrientationDescription()); setImageObserver(observer); } #if USE(CAIRO) -// Passes ownership of the native image to the caller so PassNativeImagePtr needs +// Passes ownership of the native image to the caller so NativeImagePtr needs // to be a smart pointer type. -PassNativeImagePtr SVGImage::nativeImageForCurrentFrame() +NativeImagePtr SVGImage::nativeImageForCurrentFrame(const GraphicsContext*) { if (!m_page) - return 0; + return nullptr; - std::unique_ptr<ImageBuffer> buffer = ImageBuffer::create(size(), 1); + // Cairo does not use the accelerated drawing flag, so it's OK to make an unconditionally unaccelerated buffer. + std::unique_ptr<ImageBuffer> buffer = ImageBuffer::create(size(), Unaccelerated); if (!buffer) // failed to allocate image - return 0; + return nullptr; - draw(buffer->context(), rect(), rect(), ColorSpaceDeviceRGB, CompositeSourceOver, BlendModeNormal, ImageOrientationDescription()); + draw(buffer->context(), rect(), rect(), CompositeSourceOver, BlendModeNormal, ImageOrientationDescription()); // FIXME: WK(Bug 113657): We should use DontCopyBackingStore here. return buffer->copyImage(CopyBackingStore)->nativeImageForCurrentFrame(); } #endif -void SVGImage::drawPatternForContainer(GraphicsContext* context, const FloatSize containerSize, float zoom, const FloatRect& srcRect, - const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace colorSpace, CompositeOperator compositeOp, const FloatRect& dstRect, BlendMode blendMode) +#if USE(DIRECT2D) +NativeImagePtr SVGImage::nativeImage(const GraphicsContext* targetContext) +{ + ASSERT(targetContext); + if (!m_page || !targetContext) + return nullptr; + + auto platformContext = targetContext->platformContext(); + ASSERT(platformContext); + + // Draw the SVG into a bitmap. + COMPtr<ID2D1BitmapRenderTarget> nativeImageTarget; + HRESULT hr = platformContext->CreateCompatibleRenderTarget(IntSize(rect().size()), &nativeImageTarget); + ASSERT(SUCCEEDED(hr)); + + GraphicsContext localContext(nativeImageTarget.get()); + + draw(localContext, rect(), rect(), CompositeSourceOver, BlendModeNormal, ImageOrientationDescription()); + + COMPtr<ID2D1Bitmap> nativeImage; + hr = nativeImageTarget->GetBitmap(&nativeImage); + ASSERT(SUCCEEDED(hr)); + + return nativeImage; +} +#endif + +void SVGImage::drawPatternForContainer(GraphicsContext& context, const FloatSize& containerSize, float zoom, const FloatRect& srcRect, + const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator compositeOp, const FloatRect& dstRect, BlendMode blendMode) { FloatRect zoomedContainerRect = FloatRect(FloatPoint(), containerSize); zoomedContainerRect.scale(zoom); // The ImageBuffer size needs to be scaled to match the final resolution. - AffineTransform transform = context->getCTM(); + AffineTransform transform = context.getCTM(); FloatSize imageBufferScale = FloatSize(transform.xScale(), transform.yScale()); ASSERT(imageBufferScale.width()); ASSERT(imageBufferScale.height()); @@ -196,15 +255,16 @@ void SVGImage::drawPatternForContainer(GraphicsContext* context, const FloatSize FloatRect imageBufferSize = zoomedContainerRect; imageBufferSize.scale(imageBufferScale.width(), imageBufferScale.height()); - std::unique_ptr<ImageBuffer> buffer = ImageBuffer::create(expandedIntSize(imageBufferSize.size()), 1); + std::unique_ptr<ImageBuffer> buffer = ImageBuffer::createCompatibleBuffer(expandedIntSize(imageBufferSize.size()), 1, ColorSpaceSRGB, context); if (!buffer) // Failed to allocate buffer. return; - drawForContainer(buffer->context(), containerSize, zoom, imageBufferSize, zoomedContainerRect, ColorSpaceDeviceRGB, CompositeSourceOver, BlendModeNormal); - if (context->drawLuminanceMask()) + drawForContainer(buffer->context(), containerSize, zoom, imageBufferSize, zoomedContainerRect, CompositeSourceOver, BlendModeNormal); + if (context.drawLuminanceMask()) buffer->convertToLuminanceMask(); - RefPtr<Image> image = buffer->copyImage(DontCopyBackingStore, Unscaled); - image->setSpaceSize(spaceSize()); + RefPtr<Image> image = ImageBuffer::sinkIntoImage(WTFMove(buffer), Unscaled); + if (!image) + return; // Adjust the source rect and transform due to the image buffer's scaling. FloatRect scaledSrcRect = srcRect; @@ -212,24 +272,27 @@ void SVGImage::drawPatternForContainer(GraphicsContext* context, const FloatSize AffineTransform unscaledPatternTransform(patternTransform); unscaledPatternTransform.scale(1 / imageBufferScale.width(), 1 / imageBufferScale.height()); - context->setDrawLuminanceMask(false); - image->drawPattern(context, scaledSrcRect, unscaledPatternTransform, phase, colorSpace, compositeOp, dstRect, blendMode); + context.setDrawLuminanceMask(false); + image->drawPattern(context, dstRect, scaledSrcRect, unscaledPatternTransform, phase, spacing, compositeOp, blendMode); } -void SVGImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription) +void SVGImage::draw(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription) { if (!m_page) return; FrameView* view = frameView(); + ASSERT(view); - GraphicsContextStateSaver stateSaver(*context); - context->setCompositeOperation(compositeOp, blendMode); - context->clip(enclosingIntRect(dstRect)); - bool compositingRequiresTransparencyLayer = compositeOp != CompositeSourceOver || blendMode != BlendModeNormal; + GraphicsContextStateSaver stateSaver(context); + context.setCompositeOperation(compositeOp, blendMode); + context.clip(enclosingIntRect(dstRect)); + + float alpha = context.alpha(); + bool compositingRequiresTransparencyLayer = compositeOp != CompositeSourceOver || blendMode != BlendModeNormal || alpha < 1; if (compositingRequiresTransparencyLayer) { - context->beginTransparencyLayer(1); - context->setCompositeOperation(CompositeSourceOver, BlendModeNormal); + context.beginTransparencyLayer(alpha); + context.setCompositeOperation(CompositeSourceOver, BlendModeNormal); } FloatSize scale(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height()); @@ -239,18 +302,21 @@ void SVGImage::draw(GraphicsContext* context, const FloatRect& dstRect, const Fl FloatSize topLeftOffset(srcRect.location().x() * scale.width(), srcRect.location().y() * scale.height()); FloatPoint destOffset = dstRect.location() - topLeftOffset; - context->translate(destOffset.x(), destOffset.y()); - context->scale(scale); + context.translate(destOffset.x(), destOffset.y()); + context.scale(scale); view->resize(containerSize()); + if (!m_url.isEmpty()) + view->scrollToFragment(m_url); + if (view->needsLayout()) view->layout(); - view->paint(context, enclosingIntRect(srcRect)); + view->paint(context, intersection(context.clipBounds(), enclosingIntRect(srcRect))); if (compositingRequiresTransparencyLayer) - context->endTransparencyLayer(); + context.endTransparencyLayer(); stateSaver.restore(); @@ -260,52 +326,44 @@ void SVGImage::draw(GraphicsContext* context, const FloatRect& dstRect, const Fl RenderBox* SVGImage::embeddedContentBox() const { - if (!m_page) - return 0; - SVGSVGElement* rootElement = toSVGDocument(m_page->mainFrame().document())->rootElement(); + SVGSVGElement* rootElement = this->rootElement(); if (!rootElement) - return 0; - return toRenderBox(rootElement->renderer()); + return nullptr; + return downcast<RenderBox>(rootElement->renderer()); } FrameView* SVGImage::frameView() const { if (!m_page) - return 0; + return nullptr; return m_page->mainFrame().view(); } bool SVGImage::hasRelativeWidth() const { - if (!m_page) - return false; - SVGSVGElement* rootElement = toSVGDocument(m_page->mainFrame().document())->rootElement(); + SVGSVGElement* rootElement = this->rootElement(); if (!rootElement) return false; - return rootElement->intrinsicWidth().isPercent(); + return rootElement->intrinsicWidth().isPercentOrCalculated(); } bool SVGImage::hasRelativeHeight() const { - if (!m_page) - return false; - SVGSVGElement* rootElement = toSVGDocument(m_page->mainFrame().document())->rootElement(); + SVGSVGElement* rootElement = this->rootElement(); if (!rootElement) return false; - return rootElement->intrinsicHeight().isPercent(); + return rootElement->intrinsicHeight().isPercentOrCalculated(); } void SVGImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) { - if (!m_page) - return; - SVGSVGElement* rootElement = toSVGDocument(m_page->mainFrame().document())->rootElement(); + SVGSVGElement* rootElement = this->rootElement(); if (!rootElement) return; intrinsicWidth = rootElement->intrinsicWidth(); intrinsicHeight = rootElement->intrinsicHeight(); - if (rootElement->preserveAspectRatio().align() == SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE) + if (rootElement->preserveAspectRatio().align() == SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_NONE) return; intrinsicRatio = rootElement->viewBox().size(); @@ -313,12 +371,9 @@ void SVGImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrin intrinsicRatio = FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0)); } -// FIXME: support catchUpIfNecessary. -void SVGImage::startAnimation(bool /* catchUpIfNecessary */) +void SVGImage::startAnimation() { - if (!m_page) - return; - SVGSVGElement* rootElement = toSVGDocument(m_page->mainFrame().document())->rootElement(); + SVGSVGElement* rootElement = this->rootElement(); if (!rootElement) return; rootElement->unpauseAnimations(); @@ -327,9 +382,7 @@ void SVGImage::startAnimation(bool /* catchUpIfNecessary */) void SVGImage::stopAnimation() { - if (!m_page) - return; - SVGSVGElement* rootElement = toSVGDocument(m_page->mainFrame().document())->rootElement(); + SVGSVGElement* rootElement = this->rootElement(); if (!rootElement) return; rootElement->pauseAnimations(); @@ -340,6 +393,21 @@ void SVGImage::resetAnimation() stopAnimation(); } +void SVGImage::reportApproximateMemoryCost() const +{ + Document* document = m_page->mainFrame().document(); + size_t decodedImageMemoryCost = 0; + + for (Node* node = document; node; node = NodeTraversal::next(*node)) + decodedImageMemoryCost += node->approximateMemoryCost(); + + JSC::VM& vm = commonVM(); + JSC::JSLockHolder lock(vm); + // FIXME: Adopt reportExtraMemoryVisited, and switch to reportExtraMemoryAllocated. + // https://bugs.webkit.org/show_bug.cgi?id=142595 + vm.heap.deprecatedReportExtraMemory(decodedImageMemoryCost + data()->size()); +} + bool SVGImage::dataChanged(bool allDataReceived) { // Don't do anything if is an empty image. @@ -347,10 +415,14 @@ bool SVGImage::dataChanged(bool allDataReceived) return true; if (allDataReceived) { - Page::PageClients pageClients; - fillWithEmptyClients(pageClients); + PageConfiguration pageConfiguration( + createEmptyEditorClient(), + SocketProvider::create(), + makeUniqueRef<LibWebRTCProvider>() + ); + fillWithEmptyClients(pageConfiguration); m_chromeClient = std::make_unique<SVGImageChromeClient>(this); - pageClients.chromeClient = m_chromeClient.get(); + pageConfiguration.chromeClient = m_chromeClient.get(); // FIXME: If this SVG ends up loading itself, we might leak the world. // The Cache code does not know about CachedImages holding Frames and @@ -358,10 +430,11 @@ bool SVGImage::dataChanged(bool allDataReceived) // This will become an issue when SVGImage will be able to load other // SVGImage objects, but we're safe now, because SVGImage can only be // loaded by a top-level document. - m_page = std::make_unique<Page>(pageClients); + m_page = std::make_unique<Page>(WTFMove(pageConfiguration)); m_page->settings().setMediaEnabled(false); m_page->settings().setScriptEnabled(false); m_page->settings().setPluginsEnabled(false); + m_page->settings().setAcceleratedCompositingEnabled(false); Frame& frame = m_page->mainFrame(); frame.setView(FrameView::create(frame)); @@ -380,6 +453,7 @@ bool SVGImage::dataChanged(bool allDataReceived) // Set the intrinsic size before a container size is available. m_intrinsicSize = containerSize(); + reportApproximateMemoryCost(); } return m_page != nullptr; @@ -387,7 +461,7 @@ bool SVGImage::dataChanged(bool allDataReceived) String SVGImage::filenameExtension() const { - return "svg"; + return ASCIILiteral("svg"); } bool isInSVGImage(const Element* element) @@ -401,6 +475,11 @@ bool isInSVGImage(const Element* element) return page->chrome().client().isSVGImageChromeClient(); } +void SVGImage::dump(TextStream& ts) const +{ + Image::dump(ts); + ts.dumpProperty("url", m_url.string()); } -#endif // ENABLE(SVG) + +} |