blob: c5f55b4aadc73fda59d53e3fb150d431975266d5 [file]
/*
* Copyright (C) Research In Motion Limited 2010. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "third_party/blink/renderer/core/layout/svg/svg_resources.h"
#include <memory>
#include <utility>
#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/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_effect.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();
}
FloatRect SVGResources::ReferenceBoxForEffects(
const LayoutObject& layout_object) {
// Text "sub-elements" (<tspan>, <textpath>, <a>) should use the entire
// <text>s object bounding box rather then their own.
// https://svgwg.org/svg2-draft/text.html#ObjectBoundingBoxUnitsTextObjects
const LayoutObject* obb_layout_object = &layout_object;
if (layout_object.IsSVGInline()) {
obb_layout_object =
LayoutSVGText::LocateLayoutSVGTextAncestor(&layout_object);
}
DCHECK(obb_layout_object);
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(
ToLayoutSVGResourceContainerOrNull(
directly_referenced_pattern->GetLayoutObject()));
}
}
return (!resources || !resources->HasResourceData()) ? nullptr
: std::move(resources);
}
void SVGResources::LayoutIfNeeded() {
if (clipper_filter_masker_data_) {
if (LayoutSVGResourceClipper* clipper =
clipper_filter_masker_data_->clipper)
clipper->LayoutIfNeeded();
if (LayoutSVGResourceMasker* masker = clipper_filter_masker_data_->masker)
masker->LayoutIfNeeded();
if (LayoutSVGResourceFilter* filter = clipper_filter_masker_data_->filter)
filter->LayoutIfNeeded();
}
if (marker_data_) {
if (LayoutSVGResourceMarker* marker = marker_data_->marker_start)
marker->LayoutIfNeeded();
if (LayoutSVGResourceMarker* marker = marker_data_->marker_mid)
marker->LayoutIfNeeded();
if (LayoutSVGResourceMarker* marker = marker_data_->marker_end)
marker->LayoutIfNeeded();
}
if (fill_stroke_data_) {
if (LayoutSVGResourcePaintServer* fill = fill_stroke_data_->fill)
fill->LayoutIfNeeded();
if (LayoutSVGResourcePaintServer* stroke = fill_stroke_data_->stroke)
stroke->LayoutIfNeeded();
}
if (linked_resource_)
linked_resource_->LayoutIfNeeded();
}
bool SVGResources::DifferenceNeedsLayout(const SVGResources* a,
const SVGResources* b) {
bool a_has_bounds_affecting_resource = a && a->clipper_filter_masker_data_;
bool b_has_bounds_affecting_resource = b && b->clipper_filter_masker_data_;
if (a_has_bounds_affecting_resource != b_has_bounds_affecting_resource)
return true;
if (!a_has_bounds_affecting_resource)
return false;
return a->Clipper() != b->Clipper() || a->Filter() != b->Filter() ||
a->Masker() != b->Masker();
}
InvalidationModeMask SVGResources::RemoveClientFromCacheAffectingObjectBounds(
SVGElementResourceClient& client) const {
if (!clipper_filter_masker_data_)
return 0;
InvalidationModeMask invalidation_flags =
SVGResourceClient::kBoundariesInvalidation;
if (clipper_filter_masker_data_->filter) {
if (client.ClearFilterData())
invalidation_flags |= SVGResourceClient::kPaintInvalidation;
}
return invalidation_flags;
}
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) {
if (!clipper)
return;
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) {
if (!filter)
return;
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) {
if (!marker_start)
return;
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) {
if (!marker_mid)
return;
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) {
if (!marker_end)
return;
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) {
if (!masker)
return;
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) {
if (!fill)
return;
if (!fill_stroke_data_)
fill_stroke_data_ = std::make_unique<FillStrokeData>();
fill_stroke_data_->fill = fill;
}
void SVGResources::SetStroke(LayoutSVGResourcePaintServer* stroke) {
if (!stroke)
return;
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) {
const bool had_client = element.GetSVGResourceClient();
if (auto* reference_clip =
DynamicTo<ReferenceClipPathOperation>(style.ClipPath()))
reference_clip->AddClient(element.EnsureSVGResourceClient());
if (style.HasFilter())
style.Filter().AddClient(element.EnsureSVGResourceClient());
if (StyleSVGResource* masker_resource = style.SvgStyle().MaskerResource())
masker_resource->AddClient(element.EnsureSVGResourceClient());
if (had_client)
ClearClipPathFilterMask(element, old_style);
}
void SVGResources::ClearClipPathFilterMask(SVGElement& element,
const ComputedStyle* style) {
if (!style)
return;
SVGResourceClient* client = element.GetSVGResourceClient();
if (!client)
return;
if (auto* old_reference_clip =
DynamicTo<ReferenceClipPathOperation>(style->ClipPath()))
old_reference_clip->RemoveClient(*client);
if (style->HasFilter())
style->Filter().RemoveClient(*client);
if (StyleSVGResource* masker_resource = style->SvgStyle().MaskerResource())
masker_resource->RemoveClient(*client);
}
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())
paint_resource->AddClient(element.EnsureSVGResourceClient());
if (StyleSVGResource* paint_resource = svg_style.StrokePaint().Resource())
paint_resource->AddClient(element.EnsureSVGResourceClient());
if (had_client)
ClearPaints(element, old_style);
}
void SVGResources::ClearPaints(SVGElement& element,
const ComputedStyle* style) {
if (!style)
return;
SVGResourceClient* client = element.GetSVGResourceClient();
if (!client)
return;
const SVGComputedStyle& old_svg_style = style->SvgStyle();
if (StyleSVGResource* paint_resource = old_svg_style.FillPaint().Resource())
paint_resource->RemoveClient(*client);
if (StyleSVGResource* paint_resource = old_svg_style.StrokePaint().Resource())
paint_resource->RemoveClient(*client);
}
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())
marker_resource->AddClient(element.EnsureSVGResourceClient());
if (StyleSVGResource* marker_resource = svg_style.MarkerMidResource())
marker_resource->AddClient(element.EnsureSVGResourceClient());
if (StyleSVGResource* marker_resource = svg_style.MarkerEndResource())
marker_resource->AddClient(element.EnsureSVGResourceClient());
if (had_client)
ClearMarkers(element, old_style);
}
void SVGResources::ClearMarkers(SVGElement& element,
const ComputedStyle* style) {
if (!style)
return;
SVGResourceClient* client = element.GetSVGResourceClient();
if (!client)
return;
const SVGComputedStyle& old_svg_style = style->SvgStyle();
if (StyleSVGResource* marker_resource = old_svg_style.MarkerStartResource())
marker_resource->RemoveClient(*client);
if (StyleSVGResource* marker_resource = old_svg_style.MarkerMidResource())
marker_resource->RemoveClient(*client);
if (StyleSVGResource* marker_resource = old_svg_style.MarkerEndResource())
marker_resource->RemoveClient(*client);
}
void FilterData::Trace(Visitor* visitor) {
visitor->Trace(last_effect);
visitor->Trace(node_map);
}
void FilterData::Dispose() {
node_map = nullptr;
if (last_effect)
last_effect->DisposeImageFiltersRecursive();
last_effect = nullptr;
}
SVGElementResourceClient::SVGElementResourceClient(SVGElement* element)
: element_(element) {}
void SVGElementResourceClient::ResourceContentChanged(
InvalidationModeMask invalidation_mask) {
LayoutObject* layout_object = element_->GetLayoutObject();
if (!layout_object)
return;
if (layout_object->IsSVGResourceContainer()) {
ToLayoutSVGResourceContainer(layout_object)->RemoveAllClientsFromCache();
return;
}
LayoutSVGResourceContainer::MarkClientForInvalidation(*layout_object,
invalidation_mask);
ClearFilterData();
bool needs_layout =
invalidation_mask & SVGResourceClient::kLayoutInvalidation;
LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation(
*layout_object, needs_layout);
}
void SVGElementResourceClient::ResourceElementChanged() {
if (LayoutObject* layout_object = element_->GetLayoutObject()) {
ClearFilterData();
SVGResourcesCache::ResourceReferenceChanged(*layout_object);
}
}
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(
SVGFilterPrimitiveStandardAttributes& primitive,
const QualifiedName& attribute) {
if (filter_data_ && filter_data_->state_ == FilterData::kReadyToPaint) {
SVGFilterGraphNodeMap* node_map = filter_data_->node_map;
if (FilterEffect* effect = node_map->EffectForElement(primitive)) {
if (!primitive.SetFilterEffectAttribute(effect, attribute))
return; // No change
node_map->InvalidateDependentEffects(effect);
}
}
LayoutObject* layout_object = element_->GetLayoutObject();
if (!layout_object)
return;
LayoutSVGResourceContainer::MarkClientForInvalidation(
*layout_object, SVGResourceClient::kPaintInvalidation);
}
void SVGElementResourceClient::SetFilterData(FilterData* filter_data) {
filter_data_ = filter_data;
}
bool SVGElementResourceClient::ClearFilterData() {
FilterData* filter_data = filter_data_.Release();
if (filter_data)
filter_data->Dispose();
return !!filter_data;
}
void SVGElementResourceClient::Trace(Visitor* visitor) {
visitor->Trace(element_);
visitor->Trace(filter_data_);
SVGResourceClient::Trace(visitor);
}
} // namespace blink