summaryrefslogtreecommitdiff
path: root/Source/WebCore/svg/graphics/SVGImage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/svg/graphics/SVGImage.cpp')
-rw-r--r--Source/WebCore/svg/graphics/SVGImage.cpp251
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)
+
+}