diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc | 707 |
1 files changed, 142 insertions, 565 deletions
diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc index 988c1c9b70c..31f880fb4ad 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc @@ -19,41 +19,25 @@ #include "third_party/blink/renderer/core/layout/svg/svg_resources.h" -#include <memory> -#include <utility> - +#include "base/ranges/algorithm.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h" -#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h" -#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.h" -#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_text.h" -#include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h" #include "third_party/blink/renderer/core/paint/filter_effect_builder.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/core/style/reference_clip_path_operation.h" #include "third_party/blink/renderer/core/style/style_svg_resource.h" #include "third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.h" #include "third_party/blink/renderer/core/svg/svg_filter_primitive_standard_attributes.h" -#include "third_party/blink/renderer/core/svg/svg_pattern_element.h" #include "third_party/blink/renderer/core/svg/svg_resource.h" -#include "third_party/blink/renderer/core/svg/svg_tree_scope_resources.h" -#include "third_party/blink/renderer/core/svg/svg_uri_reference.h" -#include "third_party/blink/renderer/core/svg_names.h" #include "third_party/blink/renderer/platform/graphics/filters/filter.h" #include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h" #include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h" #include "third_party/blink/renderer/platform/graphics/filters/source_graphic.h" -#if DCHECK_IS_ON() -#include <stdio.h> -#endif - namespace blink { -SVGResources::SVGResources() : linked_resource_(nullptr) {} - SVGElementResourceClient* SVGResources::GetClient(const LayoutObject& object) { return To<SVGElement>(object.GetNode())->GetSVGResourceClient(); } @@ -79,451 +63,6 @@ FloatRect SVGResources::ReferenceBoxForEffects( return obb_layout_object->ObjectBoundingBox(); } -static HashSet<AtomicString>& ClipperFilterMaskerTags() { - DEFINE_STATIC_LOCAL( - HashSet<AtomicString>, tag_list, - ({ - // "container elements": - // http://www.w3.org/TR/SVG11/intro.html#TermContainerElement - // "graphics elements" : - // http://www.w3.org/TR/SVG11/intro.html#TermGraphicsElement - svg_names::kATag.LocalName(), svg_names::kCircleTag.LocalName(), - svg_names::kEllipseTag.LocalName(), svg_names::kGTag.LocalName(), - svg_names::kImageTag.LocalName(), svg_names::kLineTag.LocalName(), - svg_names::kMarkerTag.LocalName(), svg_names::kMaskTag.LocalName(), - svg_names::kPathTag.LocalName(), svg_names::kPolygonTag.LocalName(), - svg_names::kPolylineTag.LocalName(), svg_names::kRectTag.LocalName(), - svg_names::kSVGTag.LocalName(), svg_names::kTextTag.LocalName(), - svg_names::kUseTag.LocalName(), - // Not listed in the definitions is the clipPath element, the SVG spec - // says though: - // The "clipPath" element or any of its children can specify property - // "clip-path". - // So we have to add kClipPathTag here, otherwhise clip-path on - // clipPath will fail. (Already mailed SVG WG, waiting for a solution) - svg_names::kClipPathTag.LocalName(), - // Not listed in the definitions are the text content elements, though - // filter/clipper/masker on tspan/text/.. is allowed. - // (Already mailed SVG WG, waiting for a solution) - svg_names::kTextPathTag.LocalName(), svg_names::kTSpanTag.LocalName(), - // Not listed in the definitions is the foreignObject element, but - // clip-path is a supported attribute. - svg_names::kForeignObjectTag.LocalName(), - // Elements that we ignore, as it doesn't make any sense. - // defs, pattern, switch (FIXME: Mail SVG WG about these) - // symbol (is converted to a svg element, when referenced by use, we - // can safely ignore it.) - })); - return tag_list; -} - -bool SVGResources::SupportsMarkers(const SVGElement& element) { - DEFINE_STATIC_LOCAL(HashSet<AtomicString>, tag_list, - ({ - svg_names::kLineTag.LocalName(), - svg_names::kPathTag.LocalName(), - svg_names::kPolygonTag.LocalName(), - svg_names::kPolylineTag.LocalName(), - })); - return tag_list.Contains(element.localName()); -} - -static HashSet<AtomicString>& FillAndStrokeTags() { - DEFINE_STATIC_LOCAL(HashSet<AtomicString>, tag_list, - ({ - svg_names::kCircleTag.LocalName(), - svg_names::kEllipseTag.LocalName(), - svg_names::kLineTag.LocalName(), - svg_names::kPathTag.LocalName(), - svg_names::kPolygonTag.LocalName(), - svg_names::kPolylineTag.LocalName(), - svg_names::kRectTag.LocalName(), - svg_names::kTextTag.LocalName(), - svg_names::kTextPathTag.LocalName(), - svg_names::kTSpanTag.LocalName(), - })); - return tag_list; -} - -bool SVGResources::HasResourceData() const { - return clipper_filter_masker_data_ || marker_data_ || fill_stroke_data_ || - linked_resource_; -} - -static inline SVGResources& EnsureResources( - std::unique_ptr<SVGResources>& resources) { - if (!resources) - resources = std::make_unique<SVGResources>(); - - return *resources.get(); -} - -std::unique_ptr<SVGResources> SVGResources::BuildResources( - const LayoutObject& object, - const ComputedStyle& computed_style) { - Node* node = object.GetNode(); - DCHECK(node); - SECURITY_DCHECK(node->IsSVGElement()); - - auto& element = To<SVGElement>(*node); - - const AtomicString& tag_name = element.localName(); - DCHECK(!tag_name.IsNull()); - - const SVGComputedStyle& style = computed_style.SvgStyle(); - - std::unique_ptr<SVGResources> resources; - if (ClipperFilterMaskerTags().Contains(tag_name)) { - if (computed_style.ClipPath() && !object.IsSVGRoot()) { - if (LayoutSVGResourceClipper* clipper = - GetSVGResourceAsType(computed_style.ClipPath())) { - EnsureResources(resources).SetClipper(clipper); - } - } - - if (computed_style.HasFilter() && !object.IsSVGRoot()) { - if (LayoutSVGResourceFilter* filter = - GetFilterResourceForSVG(computed_style)) { - EnsureResources(resources).SetFilter(filter); - } - } - - if (auto* masker = GetSVGResourceAsType<LayoutSVGResourceMasker>( - style.MaskerResource())) { - EnsureResources(resources).SetMasker(masker); - } - } - - if (style.HasMarkers() && SupportsMarkers(element)) { - if (auto* marker = GetSVGResourceAsType<LayoutSVGResourceMarker>( - style.MarkerStartResource())) { - EnsureResources(resources).SetMarkerStart(marker); - } - if (auto* marker = GetSVGResourceAsType<LayoutSVGResourceMarker>( - style.MarkerMidResource())) { - EnsureResources(resources).SetMarkerMid(marker); - } - if (auto* marker = GetSVGResourceAsType<LayoutSVGResourceMarker>( - style.MarkerEndResource())) { - EnsureResources(resources).SetMarkerEnd(marker); - } - } - - if (FillAndStrokeTags().Contains(tag_name)) { - if (auto* paint_resource = - GetSVGResourceAsType<LayoutSVGResourcePaintServer>( - style.FillPaint().Resource())) { - EnsureResources(resources).SetFill(paint_resource); - } - - if (auto* paint_resource = - GetSVGResourceAsType<LayoutSVGResourcePaintServer>( - style.StrokePaint().Resource())) { - EnsureResources(resources).SetStroke(paint_resource); - } - } - - if (auto* pattern = DynamicTo<SVGPatternElement>(element)) { - const SVGPatternElement* directly_referenced_pattern = - pattern->ReferencedElement(); - if (directly_referenced_pattern) { - EnsureResources(resources).SetLinkedResource( - DynamicTo<LayoutSVGResourceContainer>( - directly_referenced_pattern->GetLayoutObject())); - } - } - - return (!resources || !resources->HasResourceData()) ? nullptr - : std::move(resources); -} - -void SVGResources::ResourceDestroyed(LayoutSVGResourceContainer* resource) { - DCHECK(resource); - if (!HasResourceData()) - return; - - if (linked_resource_ == resource) { - DCHECK(!clipper_filter_masker_data_); - DCHECK(!marker_data_); - DCHECK(!fill_stroke_data_); - linked_resource_->RemoveAllClientsFromCache(); - linked_resource_ = nullptr; - return; - } - - switch (resource->ResourceType()) { - case kMaskerResourceType: - if (!clipper_filter_masker_data_) - break; - if (clipper_filter_masker_data_->masker == resource) - clipper_filter_masker_data_->masker = nullptr; - break; - case kMarkerResourceType: - if (!marker_data_) - break; - if (marker_data_->marker_start == resource) - marker_data_->marker_start = nullptr; - if (marker_data_->marker_mid == resource) - marker_data_->marker_mid = nullptr; - if (marker_data_->marker_end == resource) - marker_data_->marker_end = nullptr; - break; - case kPatternResourceType: - case kLinearGradientResourceType: - case kRadialGradientResourceType: - if (!fill_stroke_data_) - break; - if (fill_stroke_data_->fill == resource) - fill_stroke_data_->fill = nullptr; - if (fill_stroke_data_->stroke == resource) - fill_stroke_data_->stroke = nullptr; - break; - case kFilterResourceType: - if (!clipper_filter_masker_data_) - break; - if (clipper_filter_masker_data_->filter == resource) - clipper_filter_masker_data_->filter = nullptr; - break; - case kClipperResourceType: - if (!clipper_filter_masker_data_) - break; - if (clipper_filter_masker_data_->clipper == resource) - clipper_filter_masker_data_->clipper = nullptr; - break; - default: - NOTREACHED(); - } -} - -void SVGResources::ClearReferencesTo(LayoutSVGResourceContainer* resource) { - DCHECK(resource); - if (linked_resource_ == resource) { - DCHECK(!clipper_filter_masker_data_); - DCHECK(!marker_data_); - DCHECK(!fill_stroke_data_); - linked_resource_ = nullptr; - return; - } - - switch (resource->ResourceType()) { - case kMaskerResourceType: - DCHECK(clipper_filter_masker_data_); - DCHECK_EQ(clipper_filter_masker_data_->masker, resource); - clipper_filter_masker_data_->masker = nullptr; - break; - case kMarkerResourceType: - DCHECK(marker_data_); - DCHECK(resource == MarkerStart() || resource == MarkerMid() || - resource == MarkerEnd()); - if (marker_data_->marker_start == resource) - marker_data_->marker_start = nullptr; - if (marker_data_->marker_mid == resource) - marker_data_->marker_mid = nullptr; - if (marker_data_->marker_end == resource) - marker_data_->marker_end = nullptr; - break; - case kPatternResourceType: - case kLinearGradientResourceType: - case kRadialGradientResourceType: - DCHECK(fill_stroke_data_); - DCHECK(resource == Fill() || resource == Stroke()); - if (fill_stroke_data_->fill == resource) - fill_stroke_data_->fill = nullptr; - if (fill_stroke_data_->stroke == resource) - fill_stroke_data_->stroke = nullptr; - break; - case kFilterResourceType: - DCHECK(clipper_filter_masker_data_); - DCHECK_EQ(clipper_filter_masker_data_->filter, resource); - clipper_filter_masker_data_->filter = nullptr; - break; - case kClipperResourceType: - DCHECK(clipper_filter_masker_data_); - DCHECK_EQ(clipper_filter_masker_data_->clipper, resource); - clipper_filter_masker_data_->clipper = nullptr; - break; - default: - NOTREACHED(); - } -} - -void SVGResources::BuildSetOfResources( - HashSet<LayoutSVGResourceContainer*>& set) { - if (!HasResourceData()) - return; - - if (linked_resource_) { - DCHECK(!clipper_filter_masker_data_); - DCHECK(!marker_data_); - DCHECK(!fill_stroke_data_); - set.insert(linked_resource_); - return; - } - - if (clipper_filter_masker_data_) { - if (clipper_filter_masker_data_->clipper) - set.insert(clipper_filter_masker_data_->clipper); - if (clipper_filter_masker_data_->filter) - set.insert(clipper_filter_masker_data_->filter); - if (clipper_filter_masker_data_->masker) - set.insert(clipper_filter_masker_data_->masker); - } - - if (marker_data_) { - if (marker_data_->marker_start) - set.insert(marker_data_->marker_start); - if (marker_data_->marker_mid) - set.insert(marker_data_->marker_mid); - if (marker_data_->marker_end) - set.insert(marker_data_->marker_end); - } - - if (fill_stroke_data_) { - if (fill_stroke_data_->fill) - set.insert(fill_stroke_data_->fill); - if (fill_stroke_data_->stroke) - set.insert(fill_stroke_data_->stroke); - } -} - -void SVGResources::SetClipper(LayoutSVGResourceClipper* clipper) { - DCHECK(clipper); - DCHECK_EQ(clipper->ResourceType(), kClipperResourceType); - - if (!clipper_filter_masker_data_) - clipper_filter_masker_data_ = std::make_unique<ClipperFilterMaskerData>(); - - clipper_filter_masker_data_->clipper = clipper; -} - -void SVGResources::SetFilter(LayoutSVGResourceFilter* filter) { - DCHECK(filter); - DCHECK_EQ(filter->ResourceType(), kFilterResourceType); - - if (!clipper_filter_masker_data_) - clipper_filter_masker_data_ = std::make_unique<ClipperFilterMaskerData>(); - - clipper_filter_masker_data_->filter = filter; -} - -void SVGResources::SetMarkerStart(LayoutSVGResourceMarker* marker_start) { - DCHECK(marker_start); - DCHECK_EQ(marker_start->ResourceType(), kMarkerResourceType); - - if (!marker_data_) - marker_data_ = std::make_unique<MarkerData>(); - - marker_data_->marker_start = marker_start; -} - -void SVGResources::SetMarkerMid(LayoutSVGResourceMarker* marker_mid) { - DCHECK(marker_mid); - DCHECK_EQ(marker_mid->ResourceType(), kMarkerResourceType); - - if (!marker_data_) - marker_data_ = std::make_unique<MarkerData>(); - - marker_data_->marker_mid = marker_mid; -} - -void SVGResources::SetMarkerEnd(LayoutSVGResourceMarker* marker_end) { - DCHECK(marker_end); - DCHECK_EQ(marker_end->ResourceType(), kMarkerResourceType); - - if (!marker_data_) - marker_data_ = std::make_unique<MarkerData>(); - - marker_data_->marker_end = marker_end; -} - -void SVGResources::SetMasker(LayoutSVGResourceMasker* masker) { - DCHECK(masker); - DCHECK_EQ(masker->ResourceType(), kMaskerResourceType); - - if (!clipper_filter_masker_data_) - clipper_filter_masker_data_ = std::make_unique<ClipperFilterMaskerData>(); - - clipper_filter_masker_data_->masker = masker; -} - -void SVGResources::SetFill(LayoutSVGResourcePaintServer* fill) { - DCHECK(fill); - - if (!fill_stroke_data_) - fill_stroke_data_ = std::make_unique<FillStrokeData>(); - - fill_stroke_data_->fill = fill; -} - -void SVGResources::SetStroke(LayoutSVGResourcePaintServer* stroke) { - DCHECK(stroke); - - if (!fill_stroke_data_) - fill_stroke_data_ = std::make_unique<FillStrokeData>(); - - fill_stroke_data_->stroke = stroke; -} - -void SVGResources::SetLinkedResource( - LayoutSVGResourceContainer* linked_resource) { - if (!linked_resource) - return; - - linked_resource_ = linked_resource; -} - -#if DCHECK_IS_ON() -void SVGResources::Dump(const LayoutObject* object) { - DCHECK(object); - DCHECK(object->GetNode()); - - fprintf(stderr, "-> this=%p, SVGResources(layoutObject=%p, node=%p)\n", this, - object, object->GetNode()); - fprintf(stderr, " | DOM Tree:\n"); - fprintf(stderr, "%s", - object->GetNode()->ToTreeStringForThis().Utf8().c_str()); - - fprintf(stderr, "\n | List of resources:\n"); - if (clipper_filter_masker_data_) { - if (LayoutSVGResourceClipper* clipper = - clipper_filter_masker_data_->clipper) - fprintf(stderr, " |-> Clipper : %p (node=%p)\n", clipper, - clipper->GetElement()); - if (LayoutSVGResourceFilter* filter = clipper_filter_masker_data_->filter) - fprintf(stderr, " |-> Filter : %p (node=%p)\n", filter, - filter->GetElement()); - if (LayoutSVGResourceMasker* masker = clipper_filter_masker_data_->masker) - fprintf(stderr, " |-> Masker : %p (node=%p)\n", masker, - masker->GetElement()); - } - - if (marker_data_) { - if (LayoutSVGResourceMarker* marker_start = marker_data_->marker_start) - fprintf(stderr, " |-> MarkerStart: %p (node=%p)\n", marker_start, - marker_start->GetElement()); - if (LayoutSVGResourceMarker* marker_mid = marker_data_->marker_mid) - fprintf(stderr, " |-> MarkerMid : %p (node=%p)\n", marker_mid, - marker_mid->GetElement()); - if (LayoutSVGResourceMarker* marker_end = marker_data_->marker_end) - fprintf(stderr, " |-> MarkerEnd : %p (node=%p)\n", marker_end, - marker_end->GetElement()); - } - - if (fill_stroke_data_) { - if (LayoutSVGResourcePaintServer* fill = fill_stroke_data_->fill) - fprintf(stderr, " |-> Fill : %p (node=%p)\n", fill, - fill->GetElement()); - if (LayoutSVGResourcePaintServer* stroke = fill_stroke_data_->stroke) - fprintf(stderr, " |-> Stroke : %p (node=%p)\n", stroke, - stroke->GetElement()); - } - - if (linked_resource_) - fprintf(stderr, " |-> xlink:href : %p (node=%p)\n", linked_resource_, - linked_resource_->GetElement()); -} -#endif - void SVGResources::UpdateClipPathFilterMask(SVGElement& element, const ComputedStyle* old_style, const ComputedStyle& style) { @@ -543,7 +82,7 @@ void SVGResources::UpdateClipPathFilterMask(SVGElement& element, layout_object->SetNeedsPaintPropertyUpdate(); client.MarkFilterDataDirty(); } - if (StyleSVGResource* masker_resource = style.SvgStyle().MaskerResource()) + if (StyleSVGResource* masker_resource = style.MaskerResource()) masker_resource->AddClient(element.EnsureSVGResourceClient()); if (had_client) ClearClipPathFilterMask(element, old_style); @@ -563,7 +102,7 @@ void SVGResources::ClearClipPathFilterMask(SVGElement& element, style->Filter().RemoveClient(*client); client->InvalidateFilterData(); } - if (StyleSVGResource* masker_resource = style->SvgStyle().MaskerResource()) + if (StyleSVGResource* masker_resource = style->MaskerResource()) masker_resource->RemoveClient(*client); } @@ -571,10 +110,9 @@ void SVGResources::UpdatePaints(SVGElement& element, const ComputedStyle* old_style, const ComputedStyle& style) { const bool had_client = element.GetSVGResourceClient(); - const SVGComputedStyle& svg_style = style.SvgStyle(); - if (StyleSVGResource* paint_resource = svg_style.FillPaint().Resource()) + if (StyleSVGResource* paint_resource = style.FillPaint().Resource()) paint_resource->AddClient(element.EnsureSVGResourceClient()); - if (StyleSVGResource* paint_resource = svg_style.StrokePaint().Resource()) + if (StyleSVGResource* paint_resource = style.StrokePaint().Resource()) paint_resource->AddClient(element.EnsureSVGResourceClient()); if (had_client) ClearPaints(element, old_style); @@ -587,10 +125,9 @@ void SVGResources::ClearPaints(SVGElement& element, SVGResourceClient* client = element.GetSVGResourceClient(); if (!client) return; - const SVGComputedStyle& old_svg_style = style->SvgStyle(); - if (StyleSVGResource* paint_resource = old_svg_style.FillPaint().Resource()) + if (StyleSVGResource* paint_resource = style->FillPaint().Resource()) paint_resource->RemoveClient(*client); - if (StyleSVGResource* paint_resource = old_svg_style.StrokePaint().Resource()) + if (StyleSVGResource* paint_resource = style->StrokePaint().Resource()) paint_resource->RemoveClient(*client); } @@ -598,12 +135,11 @@ void SVGResources::UpdateMarkers(SVGElement& element, const ComputedStyle* old_style, const ComputedStyle& style) { const bool had_client = element.GetSVGResourceClient(); - const SVGComputedStyle& svg_style = style.SvgStyle(); - if (StyleSVGResource* marker_resource = svg_style.MarkerStartResource()) + if (StyleSVGResource* marker_resource = style.MarkerStartResource()) marker_resource->AddClient(element.EnsureSVGResourceClient()); - if (StyleSVGResource* marker_resource = svg_style.MarkerMidResource()) + if (StyleSVGResource* marker_resource = style.MarkerMidResource()) marker_resource->AddClient(element.EnsureSVGResourceClient()); - if (StyleSVGResource* marker_resource = svg_style.MarkerEndResource()) + if (StyleSVGResource* marker_resource = style.MarkerEndResource()) marker_resource->AddClient(element.EnsureSVGResourceClient()); if (had_client) ClearMarkers(element, old_style); @@ -616,58 +152,93 @@ void SVGResources::ClearMarkers(SVGElement& element, SVGResourceClient* client = element.GetSVGResourceClient(); if (!client) return; - const SVGComputedStyle& old_svg_style = style->SvgStyle(); - if (StyleSVGResource* marker_resource = old_svg_style.MarkerStartResource()) + if (StyleSVGResource* marker_resource = style->MarkerStartResource()) marker_resource->RemoveClient(*client); - if (StyleSVGResource* marker_resource = old_svg_style.MarkerMidResource()) + if (StyleSVGResource* marker_resource = style->MarkerMidResource()) marker_resource->RemoveClient(*client); - if (StyleSVGResource* marker_resource = old_svg_style.MarkerEndResource()) + if (StyleSVGResource* marker_resource = style->MarkerEndResource()) marker_resource->RemoveClient(*client); } -sk_sp<PaintFilter> FilterData::BuildPaintFilter() { - return paint_filter_builder::Build(last_effect_, kInterpolationSpaceSRGB); -} +class SVGElementResourceClient::FilterData final + : public GarbageCollected<SVGElementResourceClient::FilterData> { + public: + FilterData(FilterEffect* last_effect, SVGFilterGraphNodeMap* node_map) + : last_effect_(last_effect), node_map_(node_map) {} -bool FilterData::Invalidate(SVGFilterPrimitiveStandardAttributes& primitive, - const QualifiedName& attribute) { - if (FilterEffect* effect = node_map_->EffectForElement(primitive)) { - if (!primitive.SetFilterEffectAttribute(effect, attribute)) - return false; // No change - node_map_->InvalidateDependentEffects(effect); + sk_sp<PaintFilter> BuildPaintFilter() { + return paint_filter_builder::Build(last_effect_, kInterpolationSpaceSRGB); } - return true; -} -void FilterData::Trace(Visitor* visitor) const { - visitor->Trace(last_effect_); - visitor->Trace(node_map_); -} + // Perform a finegrained invalidation of the filter chain for the + // specified filter primitive and attribute. Returns false if no + // further invalidation is required, otherwise true. + bool Invalidate(SVGFilterPrimitiveStandardAttributes& primitive, + const QualifiedName& attribute) { + if (FilterEffect* effect = node_map_->EffectForElement(primitive)) { + if (!primitive.SetFilterEffectAttribute(effect, attribute)) + return false; // No change + node_map_->InvalidateDependentEffects(effect); + } + return true; + } -void FilterData::Dispose() { - node_map_ = nullptr; - if (last_effect_) - last_effect_->DisposeImageFiltersRecursive(); - last_effect_ = nullptr; -} + void Dispose() { + node_map_ = nullptr; + if (last_effect_) + last_effect_->DisposeImageFiltersRecursive(); + last_effect_ = nullptr; + } + + void Trace(Visitor* visitor) const { + visitor->Trace(last_effect_); + visitor->Trace(node_map_); + } + + private: + Member<FilterEffect> last_effect_; + Member<SVGFilterGraphNodeMap> node_map_; +}; SVGElementResourceClient::SVGElementResourceClient(SVGElement* element) : element_(element), filter_data_dirty_(false) {} -void SVGElementResourceClient::ResourceContentChanged( - InvalidationModeMask invalidation_mask) { +namespace { + +template <typename ContainerType> +bool ContainsResource(const ContainerType* container, SVGResource* resource) { + return container && container->Resource() == resource; +} + +bool ContainsResource(const FilterOperations& operations, + SVGResource* resource) { + return base::ranges::any_of( + operations.Operations(), [resource](const FilterOperation* operation) { + return ContainsResource(DynamicTo<ReferenceFilterOperation>(operation), + resource); + }); +} + +} // namespace + +void SVGElementResourceClient::ResourceContentChanged(SVGResource* resource) { LayoutObject* layout_object = element_->GetLayoutObject(); if (!layout_object) return; - if (layout_object->IsSVGResourceContainer()) { - To<LayoutSVGResourceContainer>(layout_object)->RemoveAllClientsFromCache(); - return; - } - if (invalidation_mask & SVGResourceClient::kFilterCacheInvalidation) + const ComputedStyle& style = layout_object->StyleRef(); + if (style.HasFilter() && ContainsResource(style.Filter(), resource)) { InvalidateFilterData(); + layout_object->SetShouldDoFullPaintInvalidation(); + } + + if (auto* container = DynamicTo<LayoutSVGResourceContainer>(layout_object)) { + container->RemoveAllClientsFromCache(); + return; + } - if (invalidation_mask & SVGResourceClient::kPaintInvalidation) { + if (ContainsResource(style.FillPaint().Resource(), resource) || + ContainsResource(style.StrokePaint().Resource(), resource)) { // Since LayoutSVGInlineTexts don't have SVGResources (they use their // parent's), they will not be notified of changes to paint servers. So // if the client is one that could have a LayoutSVGInlineText use a @@ -677,48 +248,34 @@ void SVGElementResourceClient::ResourceContentChanged( PaintInvalidationReason::kSVGResource); } - if (invalidation_mask & SVGResourceClient::kClipCacheInvalidation) + bool needs_layout = false; + if (ContainsResource(style.MarkerStartResource(), resource) || + ContainsResource(style.MarkerMidResource(), resource) || + ContainsResource(style.MarkerEndResource(), resource)) { + needs_layout = true; + layout_object->SetNeedsBoundariesUpdate(); + } + + const auto* clip_reference = + DynamicTo<ReferenceClipPathOperation>(style.ClipPath()); + if (ContainsResource(clip_reference, resource)) { + // TODO(fs): "Downgrade" to non-subtree? + layout_object->SetSubtreeShouldDoFullPaintInvalidation(); layout_object->InvalidateClipPathCache(); + } - // Invalidate paint properties to update effects if any. - if (invalidation_mask & SVGResourceClient::kPaintPropertiesInvalidation) + if (ContainsResource(style.MaskerResource(), resource)) { + // TODO(fs): "Downgrade" to non-subtree? + layout_object->SetSubtreeShouldDoFullPaintInvalidation(); layout_object->SetNeedsPaintPropertyUpdate(); + } - if (invalidation_mask & SVGResourceClient::kBoundariesInvalidation) - layout_object->SetNeedsBoundariesUpdate(); - - bool needs_layout = - invalidation_mask & SVGResourceClient::kLayoutInvalidation; LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation( *layout_object, needs_layout); } -void SVGElementResourceClient::ResourceElementChanged() { - LayoutObject* layout_object = element_->GetLayoutObject(); - if (!layout_object) - return; - // TODO(fs): If the resource element (for a filter) doesn't actually change - // we don't need to perform the associated invalidations. - InvalidateFilterData(); - if (layout_object->Parent()) { - SVGResourcesCache::UpdateResources(*layout_object); - LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation( - *layout_object, true); - } -} - -void SVGElementResourceClient::ResourceDestroyed( - LayoutSVGResourceContainer* resource) { - LayoutObject* layout_object = element_->GetLayoutObject(); - if (!layout_object) - return; - SVGResources* resources = - SVGResourcesCache::CachedResourcesForLayoutObject(*layout_object); - if (resources) - resources->ResourceDestroyed(resource); -} - void SVGElementResourceClient::FilterPrimitiveChanged( + SVGResource* resource, SVGFilterPrimitiveStandardAttributes& primitive, const QualifiedName& attribute) { if (filter_data_ && !filter_data_->Invalidate(primitive, attribute)) @@ -730,7 +287,8 @@ void SVGElementResourceClient::FilterPrimitiveChanged( MarkFilterDataDirty(); } -static FilterData* CreateFilterDataWithNodeMap( +SVGElementResourceClient::FilterData* +SVGElementResourceClient::CreateFilterDataWithNodeMap( FilterEffectBuilder& builder, const ReferenceFilterOperation& reference_filter) { auto* node_map = MakeGarbageCollected<SVGFilterGraphNodeMap>(); @@ -751,19 +309,35 @@ void SVGElementResourceClient::UpdateFilterData( if (!operations.IsEmpty() && !filter_data_dirty_ && reference_box == operations.ReferenceBox()) return; - if (!filter_data_ && GetFilterResourceForSVG(object.StyleRef())) { - FilterEffectBuilder builder(reference_box, 1); - filter_data_ = CreateFilterDataWithNodeMap( - builder, - To<ReferenceFilterOperation>(*object.StyleRef().Filter().at(0))); - } - operations.Clear(); - if (filter_data_) { - operations.AppendReferenceFilter(filter_data_->BuildPaintFilter()); + const ComputedStyle& style = object.StyleRef(); + FilterEffectBuilder builder(reference_box, 1); + builder.SetShorthandScale(1 / style.EffectiveZoom()); + const FilterOperations& filter = style.Filter(); + // If the filter is a single 'url(...)' reference we can optimize some + // mutations to the referenced filter chain by tracking the filter + // dependencies and only perform partial invalidations of the filter chain. + const bool is_single_reference_filter = + filter.size() == 1 && IsA<ReferenceFilterOperation>(*filter.at(0)); + if (is_single_reference_filter) { + if (!filter_data_) { + filter_data_ = CreateFilterDataWithNodeMap( + builder, To<ReferenceFilterOperation>(*filter.at(0))); + } + operations.Clear(); + if (filter_data_) { + operations.AppendReferenceFilter(filter_data_->BuildPaintFilter()); + } else { + // Filter construction failed. Create a filter chain that yields + // transparent black. + operations.AppendOpacityFilter(0); + } } else { - // Filter construction failed. Create a filter chain that yields - // transparent black. - operations.AppendOpacityFilter(0); + // Drop any existing filter data since the filter is no longer + // cacheable. + if (FilterData* filter_data = filter_data_.Release()) + filter_data->Dispose(); + + operations = builder.BuildFilterOperations(filter); } operations.SetReferenceBox(reference_box); filter_data_dirty_ = false; @@ -794,34 +368,37 @@ void SVGElementResourceClient::Trace(Visitor* visitor) const { } SVGResourceInvalidator::SVGResourceInvalidator(LayoutObject& object) - : resources_(SVGResourcesCache::CachedResourcesForLayoutObject(object)), - object_(object) {} + : object_(object) {} void SVGResourceInvalidator::InvalidateEffects() { - if (!resources_) - return; - if (resources_->Filter()) - SVGResources::GetClient(object_)->InvalidateFilterData(); - if (resources_->Clipper()) { + const ComputedStyle& style = object_.StyleRef(); + if (style.HasFilter()) { + if (SVGElementResourceClient* client = SVGResources::GetClient(object_)) + client->InvalidateFilterData(); + } + if (style.HasClipPath()) { object_.SetShouldDoFullPaintInvalidation(); object_.InvalidateClipPathCache(); } - if (resources_->Masker()) { + if (style.MaskerResource()) { object_.SetShouldDoFullPaintInvalidation(); object_.SetNeedsPaintPropertyUpdate(); } } void SVGResourceInvalidator::InvalidatePaints() { - if (!resources_) + SVGElementResourceClient* client = SVGResources::GetClient(object_); + if (!client) return; bool needs_invalidation = false; - SVGElementResourceClient* client = SVGResources::GetClient(object_); - if (LayoutSVGResourcePaintServer* fill = resources_->Fill()) { + const ComputedStyle& style = object_.StyleRef(); + if (auto* fill = GetSVGResourceAsType<LayoutSVGResourcePaintServer>( + *client, style.FillPaint().Resource())) { fill->RemoveClientFromCache(*client); needs_invalidation = true; } - if (LayoutSVGResourcePaintServer* stroke = resources_->Stroke()) { + if (auto* stroke = GetSVGResourceAsType<LayoutSVGResourcePaintServer>( + *client, style.StrokePaint().Resource())) { stroke->RemoveClientFromCache(*client); needs_invalidation = true; } |