Commit 42738924 authored by schenney@chromium.org's avatar schenney@chromium.org

feImage should not be allowed to self reference

https://bugs.webkit.org/show_bug.cgi?id=94652

Reviewed by Eric Seidel.

Source/WebCore: 

Add cycle detection for SVG filter application, and also fix a problem
with graphics context restore when filters are applied. This also
converts the flags in FilterData to a state tracking system, as the
number of flags was getting messy and only one flag is valid at any given time.

Test: svg/filters/feImage-self-and-other-referencing.html

* rendering/svg/RenderSVGResourceFilter.cpp: Convert to new FilterData
state management and enable cycle detection.
(WebCore):
(WebCore::RenderSVGResourceFilter::removeClientFromCache): Change isBuilt and markedForRemoval flags to state enums.
(WebCore::RenderSVGResourceFilter::applyResource): Change flags to state enums and detect cycles.
(WebCore::RenderSVGResourceFilter::postApplyResource): Change flags to state and add handling
for the various states.
(WebCore::RenderSVGResourceFilter::primitiveAttributeChanged): Change isBuilt flag to state enums.
* rendering/svg/RenderSVGResourceFilter.h:
(WebCore::FilterData::FilterData):
(FilterData): Convert to a state tracking system.
* rendering/svg/RenderSVGRoot.cpp:
(WebCore::RenderSVGRoot::paintReplaced): Add a block around the
SVGRenderingContext so that it applies the filter and reverts the
context before the calling method restores the context.

LayoutTests: 

Additional test case for situations when the filter is applied to multiple objects that it also references.

* svg/filters/feImage-self-and-other-referencing-expected.html: Added.
* svg/filters/feImage-self-and-other-referencing.html: Added.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@132856 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 90dd8df5
2012-10-25 Stephen Chenney <schenney@chromium.org>
feImage should not be allowed to self reference
https://bugs.webkit.org/show_bug.cgi?id=94652
Reviewed by Eric Seidel.
Additional test case for situations when the filter is applied to multiple objects that it also references.
* svg/filters/feImage-self-and-other-referencing-expected.html: Added.
* svg/filters/feImage-self-and-other-referencing.html: Added.
2012-10-29 Michael Saboff <msaboff@apple.com>
Non-special escape character sequences cause JSC::Lexer::parseString to create 16 bit strings
<html>
<body>
<p>PASS if no crash</p>
</body>
</html>
<html>
<head>
<script>
onload = function() {
document.getElementById("svgRoot").setAttribute('filter', 'url(#imageFilter)')
}
</script>
</head>
<body>
<p>PASS if no crash</p>
<svg width="10" height="10" id="svgRoot" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<filter id="imageFilter">
<feImage id="feImage" xlink:href="#svgRoot"/>
<feOffset dx="50" dy="50" />
</filter>
<g filter="url(#imageFilter)">
<rect x="0" y="0" width="50" height="50" fill="green" />
</g>
</svg>
</body>
</html>
2012-10-25 Stephen Chenney <schenney@chromium.org>
feImage should not be allowed to self reference
https://bugs.webkit.org/show_bug.cgi?id=94652
Reviewed by Eric Seidel.
Add cycle detection for SVG filter application, and also fix a problem
with graphics context restore when filters are applied. This also
converts the flags in FilterData to a state tracking system, as the
number of flags was getting messy and only one flag is valid at any given time.
Test: svg/filters/feImage-self-and-other-referencing.html
* rendering/svg/RenderSVGResourceFilter.cpp: Convert to new FilterData
state management and enable cycle detection.
(WebCore):
(WebCore::RenderSVGResourceFilter::removeClientFromCache): Change isBuilt and markedForRemoval flags to state enums.
(WebCore::RenderSVGResourceFilter::applyResource): Change flags to state enums and detect cycles.
(WebCore::RenderSVGResourceFilter::postApplyResource): Change flags to state and add handling
for the various states.
(WebCore::RenderSVGResourceFilter::primitiveAttributeChanged): Change isBuilt flag to state enums.
* rendering/svg/RenderSVGResourceFilter.h:
(WebCore::FilterData::FilterData):
(FilterData): Convert to a state tracking system.
* rendering/svg/RenderSVGRoot.cpp:
(WebCore::RenderSVGRoot::paintReplaced): Add a block around the
SVGRenderingContext so that it applies the filter and reverts the
context before the calling method restores the context.
2012-10-29 Dan Bernstein <mitz@apple.com>
<rdar://problem/12592716> REGRESSION (r132545): With full-page accelerated drawing, a
......@@ -57,25 +57,6 @@ using namespace std;
namespace WebCore {
class ApplyingFilterEffectGuard {
public:
ApplyingFilterEffectGuard(FilterData* data)
: m_filterData(data)
{
// The guard must be constructed when the filter is not applying.
ASSERT(!m_filterData->isApplying);
m_filterData->isApplying = true;
}
~ApplyingFilterEffectGuard()
{
ASSERT(m_filterData->isApplying);
m_filterData->isApplying = false;
}
FilterData* m_filterData;
};
RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;
RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
......@@ -108,7 +89,7 @@ void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool m
if (FilterData* filterData = m_filter.get(client)) {
if (filterData->savedContext)
filterData->markedForRemoval = true;
filterData->state = FilterData::MarkedForRemoval;
else
delete m_filter.take(client);
}
......@@ -168,14 +149,11 @@ bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*,
ASSERT(context);
ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
// Returning false here, to avoid drawings onto the context. We just want to
// draw the stored filter output, not the unfiltered object as well.
if (m_filter.contains(object)) {
FilterData* filterData = m_filter.get(object);
if (filterData->isBuilt || filterData->isApplying)
return false;
delete m_filter.take(object); // Oops, have to rebuild, go through normal code path
if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying)
filterData->state = FilterData::CycleDetected;
return false; // Already built, or we're in a cycle, or we're marked for removal. Regardless, just do nothing more now.
}
OwnPtr<FilterData> filterData(adoptPtr(new FilterData));
......@@ -292,17 +270,21 @@ void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsCo
if (!filterData)
return;
if (filterData->markedForRemoval) {
switch (filterData->state) {
case FilterData::MarkedForRemoval:
delete m_filter.take(object);
return;
}
// We have a cycle if we are already applying the data.
// This can occur due to FeImage referencing a source that makes use of the FEImage itself.
if (filterData->isApplying)
case FilterData::CycleDetected:
case FilterData::Applying:
// We have a cycle if we are already applying the data.
// This can occur due to FeImage referencing a source that makes use of the FEImage itself.
// This is the first place we've hit the cycle, so set the state back to PaintingSource so the return stack
// will continue correctly.
filterData->state = FilterData::PaintingSource;
return;
if (!filterData->isBuilt) {
case FilterData::PaintingSource:
if (!filterData->savedContext) {
removeClientFromCache(object);
return;
......@@ -310,9 +292,10 @@ void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsCo
context = filterData->savedContext;
filterData->savedContext = 0;
}
break;
ApplyingFilterEffectGuard isApplyingGuard(filterData);
case FilterData::Built: { } // Empty
}
FilterEffect* lastEffect = filterData->builder->lastEffect();
......@@ -320,11 +303,12 @@ void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsCo
// This is the real filtering of the object. It just needs to be called on the
// initial filtering process. We just take the stored filter result on a
// second drawing.
if (!filterData->isBuilt)
if (filterData->state != FilterData::Built)
filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release());
// Always true if filterData is just built (filterData->isBuilt is false).
// Always true if filterData is just built (filterData->state == FilterData::Built).
if (!lastEffect->hasResult()) {
filterData->state = FilterData::Applying;
lastEffect->apply();
lastEffect->correctFilterResultIfNeeded();
#if !USE(CG)
......@@ -333,7 +317,7 @@ void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsCo
resultImage->transformColorSpace(lastEffect->colorSpace(), ColorSpaceDeviceRGB);
#endif
}
filterData->isBuilt = true;
filterData->state = FilterData::Built;
ImageBuffer* resultImage = lastEffect->asImageBuffer();
if (resultImage) {
......@@ -365,7 +349,7 @@ void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, co
for (; it != end; ++it) {
FilterData* filterData = it->value;
if (!filterData->isBuilt)
if (filterData->state != FilterData::Built)
continue;
SVGFilterBuilder* builder = filterData->builder.get();
......
......@@ -42,11 +42,11 @@ namespace WebCore {
struct FilterData {
WTF_MAKE_FAST_ALLOCATED;
public:
enum FilterDataState { PaintingSource, Applying, Built, CycleDetected, MarkedForRemoval };
FilterData()
: savedContext(0)
, isBuilt(false)
, isApplying(false)
, markedForRemoval(false)
, state(PaintingSource)
{
}
......@@ -57,9 +57,7 @@ public:
AffineTransform shearFreeAbsoluteTransform;
FloatRect boundaries;
FloatSize scale;
bool isBuilt : 1;
bool isApplying : 1;
bool markedForRemoval : 1;
FilterDataState state;
};
class GraphicsContext;
......
......@@ -299,15 +299,19 @@ void RenderSVGRoot::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paint
IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset);
childPaintInfo.applyTransform(AffineTransform::translation(adjustedPaintOffset.x(), adjustedPaintOffset.y()) * localToBorderBoxTransform());
SVGRenderingContext renderingContext;
bool continueRendering = true;
if (childPaintInfo.phase == PaintPhaseForeground) {
renderingContext.prepareToRenderSVGContent(this, childPaintInfo);
continueRendering = renderingContext.isRenderingPrepared();
}
// SVGRenderingContext must be destroyed before we restore the childPaintInfo.context, because a filter may have
// changed the context and it is only reverted when the SVGRenderingContext destructor finishes applying the filter.
{
SVGRenderingContext renderingContext;
bool continueRendering = true;
if (childPaintInfo.phase == PaintPhaseForeground) {
renderingContext.prepareToRenderSVGContent(this, childPaintInfo);
continueRendering = renderingContext.isRenderingPrepared();
}
if (continueRendering)
RenderBox::paint(childPaintInfo, LayoutPoint());
if (continueRendering)
RenderBox::paint(childPaintInfo, LayoutPoint());
}
childPaintInfo.context->restore();
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment