SVG filter triggers unstable layout.
[WebKit-https.git] / Source / WebCore / rendering / RenderLayerFilters.cpp
1 /*
2  * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
3  * Copyright (C) 2013 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above
10  *    copyright notice, this list of conditions and the following
11  *    disclaimer.
12  * 2. Redistributions in binary form must reproduce the above
13  *    copyright notice, this list of conditions and the following
14  *    disclaimer in the documentation and/or other materials
15  *    provided with the distribution.
16  * 
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
26  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "RenderLayerFilters.h"
33
34 #include "CSSFilter.h"
35 #include "CachedSVGDocument.h"
36 #include "CachedSVGDocumentReference.h"
37 #include "Logging.h"
38 #include "RenderSVGResourceFilter.h"
39 #include <wtf/NeverDestroyed.h>
40
41 namespace WebCore {
42
43 RenderLayerFilters::RenderLayerFilters(RenderLayer& layer)
44     : m_layer(layer)
45 {
46 }
47
48 RenderLayerFilters::~RenderLayerFilters()
49 {
50     removeReferenceFilterClients();
51 }
52
53 void RenderLayerFilters::setFilter(RefPtr<CSSFilter>&& filter)
54 {
55     m_filter = WTFMove(filter);
56 }
57
58 bool RenderLayerFilters::hasFilterThatMovesPixels() const
59 {
60     return m_filter && m_filter->hasFilterThatMovesPixels();
61 }
62
63 bool RenderLayerFilters::hasFilterThatShouldBeRestrictedBySecurityOrigin() const
64 {
65     return m_filter && m_filter->hasFilterThatShouldBeRestrictedBySecurityOrigin();
66 }
67
68 void RenderLayerFilters::notifyFinished(CachedResource&)
69 {
70     // FIXME: This really shouldn't have to invalidate layer composition,
71     // but tests like css3/filters/effect-reference-delete.html fail if that doesn't happen.
72     if (auto* enclosingElement = m_layer.enclosingElement())
73         enclosingElement->invalidateStyleAndLayerComposition();
74     m_layer.renderer().repaint();
75 }
76
77 void RenderLayerFilters::updateReferenceFilterClients(const FilterOperations& operations)
78 {
79     removeReferenceFilterClients();
80
81     for (auto& operation : operations.operations()) {
82         if (!is<ReferenceFilterOperation>(*operation))
83             continue;
84
85         auto& referenceOperation = downcast<ReferenceFilterOperation>(*operation);
86         auto* documentReference = referenceOperation.cachedSVGDocumentReference();
87         if (auto* cachedSVGDocument = documentReference ? documentReference->document() : nullptr) {
88             // Reference is external; wait for notifyFinished().
89             cachedSVGDocument->addClient(*this);
90             m_externalSVGReferences.append(cachedSVGDocument);
91         } else {
92             // Reference is internal; add layer as a client so we can trigger filter repaint on SVG attribute change.
93             auto* filterElement = m_layer.renderer().document().getElementById(referenceOperation.fragment());
94             if (!filterElement)
95                 continue;
96             auto* renderer = filterElement->renderer();
97             if (!is<RenderSVGResourceFilter>(renderer))
98                 continue;
99             downcast<RenderSVGResourceFilter>(*renderer).addClientRenderLayer(&m_layer);
100             m_internalSVGReferences.append(filterElement);
101         }
102     }
103 }
104
105 void RenderLayerFilters::removeReferenceFilterClients()
106 {
107     for (auto& resourceHandle : m_externalSVGReferences)
108         resourceHandle->removeClient(*this);
109
110     m_externalSVGReferences.clear();
111
112     for (auto& filterElement : m_internalSVGReferences) {
113         if (auto* renderer = filterElement->renderer())
114             downcast<RenderSVGResourceContainer>(*renderer).removeClientRenderLayer(&m_layer);
115     }
116     m_internalSVGReferences.clear();
117 }
118
119 void RenderLayerFilters::buildFilter(RenderElement& renderer, float scaleFactor, RenderingMode renderingMode)
120 {
121     if (!m_filter) {
122         m_filter = CSSFilter::create();
123         m_filter->setFilterScale(scaleFactor);
124         m_filter->setRenderingMode(renderingMode);
125     } else if (m_filter->filterScale() != scaleFactor) {
126         m_filter->setFilterScale(scaleFactor);
127         m_filter->clearIntermediateResults();
128     }
129
130     // If the filter fails to build, remove it from the layer. It will still attempt to
131     // go through regular processing (e.g. compositing), but never apply anything.
132     // FIXME: this rebuilds the entire effects chain even if the filter style didn't change.
133     if (!m_filter->build(renderer, renderer.style().filter(), FilterConsumer::FilterProperty))
134         m_filter = nullptr;
135 }
136
137 GraphicsContext* RenderLayerFilters::beginFilterEffect(GraphicsContext& destinationContext, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect, const LayoutRect& layerRepaintRect)
138 {
139     if (!m_filter)
140         return nullptr;
141
142     auto& filter = *m_filter;
143     auto filterSourceRect = filter.computeSourceImageRectForDirtyRect(filterBoxRect, dirtyRect);
144     if (filterSourceRect.isEmpty())
145         return nullptr;
146
147     bool hasUpdatedBackingStore = filter.updateBackingStoreRect(filterSourceRect);
148     if (!filter.hasFilterThatMovesPixels())
149         m_repaintRect = dirtyRect;
150     else {
151         if (hasUpdatedBackingStore)
152             m_repaintRect = filterSourceRect;
153         else {
154             m_repaintRect = dirtyRect;
155             m_repaintRect.unite(layerRepaintRect);
156             m_repaintRect.intersect(filterSourceRect);
157         }
158     }
159     m_paintOffset = filterSourceRect.location();
160     resetDirtySourceRect();
161
162     filter.determineFilterPrimitiveSubregion();
163
164     filter.allocateBackingStoreIfNeeded(destinationContext);
165     auto* sourceGraphicsContext = filter.inputContext();
166     if (!sourceGraphicsContext || filter.filterRegion().isEmpty() || ImageBuffer::sizeNeedsClamping(filter.filterRegion().size()))
167         return nullptr;
168
169     // Translate the context so that the contents of the layer is captured in the offscreen memory buffer.
170     sourceGraphicsContext->save();
171     sourceGraphicsContext->translate(-m_paintOffset);
172     sourceGraphicsContext->clearRect(m_repaintRect);
173     sourceGraphicsContext->clip(m_repaintRect);
174
175     return sourceGraphicsContext;
176 }
177
178 void RenderLayerFilters::applyFilterEffect(GraphicsContext& destinationContext)
179 {
180     ASSERT(m_filter->inputContext());
181
182     LOG_WITH_STREAM(Filters, stream << "\nRenderLayerFilters " << this << " applyFilterEffect");
183
184     auto& filter = *m_filter;
185     filter.inputContext()->restore();
186
187     filter.apply();
188
189     // Get the filtered output and draw it in place.
190     LayoutRect destRect = filter.outputRect();
191     destRect.move(m_paintOffset.x(), m_paintOffset.y());
192
193     if (auto* outputBuffer = filter.output())
194         destinationContext.drawImageBuffer(*outputBuffer, snapRectToDevicePixels(destRect, m_layer.renderer().document().deviceScaleFactor()));
195
196     filter.clearIntermediateResults();
197
198     LOG_WITH_STREAM(Filters, stream << "RenderLayerFilters " << this << " applyFilterEffect done\n");
199 }
200
201 } // namespace WebCore