blob: 2d2add6f7402eb01c554e7f9571a6b56aa72b05f [file] [log] [blame]
/*
* 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/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/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"
#ifndef NDEBUG
#include <stdio.h>
#endif
namespace blink {
using namespace SVGNames;
SVGResources::SVGResources() : linked_resource_(nullptr) {}
SVGResourceClient* SVGResources::GetClient(const LayoutObject& object) {
return ToSVGElement(object.GetNode())->GetSVGResourceClient();
}
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
aTag.LocalName(), circleTag.LocalName(), ellipseTag.LocalName(),
gTag.LocalName(), imageTag.LocalName(), lineTag.LocalName(),
markerTag.LocalName(), maskTag.LocalName(), pathTag.LocalName(),
polygonTag.LocalName(), polylineTag.LocalName(), rectTag.LocalName(),
svgTag.LocalName(), textTag.LocalName(), useTag.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 clipPathTag here, otherwhise clip-path on
// clipPath will fail. (Already mailed SVG WG, waiting for a solution)
clipPathTag.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)
textPathTag.LocalName(), tspanTag.LocalName(),
// Not listed in the definitions is the foreignObject element, but
// clip-path is a supported attribute.
foreignObjectTag.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,
({
lineTag.LocalName(), pathTag.LocalName(),
polygonTag.LocalName(), polylineTag.LocalName(),
}));
return tag_list.Contains(element.localName());
}
static HashSet<AtomicString>& FillAndStrokeTags() {
DEFINE_STATIC_LOCAL(
HashSet<AtomicString>, tag_list,
({
circleTag.LocalName(), ellipseTag.LocalName(), lineTag.LocalName(),
pathTag.LocalName(), polygonTag.LocalName(), polylineTag.LocalName(),
rectTag.LocalName(), textTag.LocalName(), textPathTag.LocalName(),
tspanTag.LocalName(),
}));
return tag_list;
}
namespace {
template <typename ContainerType>
bool IsResourceOfType(LayoutSVGResourceContainer* container) {
return container->ResourceType() == ContainerType::kResourceType;
}
template <>
bool IsResourceOfType<LayoutSVGResourcePaintServer>(
LayoutSVGResourceContainer* container) {
return container->IsSVGPaintServer();
}
template <typename ContainerType>
ContainerType* CastResource(SVGResource* resource) {
if (!resource)
return nullptr;
if (LayoutSVGResourceContainer* container = resource->ResourceContainer()) {
if (IsResourceOfType<ContainerType>(container))
return static_cast<ContainerType*>(container);
}
return nullptr;
}
template <typename ContainerType>
ContainerType* CastResource(StyleSVGResource& style_resource) {
return CastResource<ContainerType>(style_resource.Resource());
}
} // namespace
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());
SVGElement& element = ToSVGElement(*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()) {
ClipPathOperation* clip_path_operation = computed_style.ClipPath();
if (clip_path_operation->GetType() == ClipPathOperation::REFERENCE) {
const ReferenceClipPathOperation& clip_path_reference =
ToReferenceClipPathOperation(*clip_path_operation);
EnsureResources(resources).SetClipper(
CastResource<LayoutSVGResourceClipper>(
clip_path_reference.Resource()));
}
}
if (computed_style.HasFilter() && !object.IsSVGRoot()) {
const FilterOperations& filter_operations = computed_style.Filter();
if (filter_operations.size() == 1) {
const FilterOperation& filter_operation = *filter_operations.at(0);
if (filter_operation.GetType() == FilterOperation::REFERENCE) {
const auto& reference_filter_operation =
ToReferenceFilterOperation(filter_operation);
EnsureResources(resources).SetFilter(
CastResource<LayoutSVGResourceFilter>(
reference_filter_operation.Resource()));
}
}
}
if (StyleSVGResource* masker_resource = style.MaskerResource()) {
EnsureResources(resources).SetMasker(
CastResource<LayoutSVGResourceMasker>(*masker_resource));
}
}
if (style.HasMarkers() && SupportsMarkers(element)) {
if (StyleSVGResource* marker_start_resource = style.MarkerStartResource()) {
EnsureResources(resources).SetMarkerStart(
CastResource<LayoutSVGResourceMarker>(*marker_start_resource));
}
if (StyleSVGResource* marker_mid_resource = style.MarkerMidResource()) {
EnsureResources(resources).SetMarkerMid(
CastResource<LayoutSVGResourceMarker>(*marker_mid_resource));
}
if (StyleSVGResource* marker_end_resource = style.MarkerEndResource()) {
EnsureResources(resources).SetMarkerEnd(
CastResource<LayoutSVGResourceMarker>(*marker_end_resource));
}
}
if (FillAndStrokeTags().Contains(tag_name)) {
if (StyleSVGResource* fill_resource = style.FillPaint().Resource()) {
EnsureResources(resources).SetFill(
CastResource<LayoutSVGResourcePaintServer>(*fill_resource));
}
if (StyleSVGResource* stroke_resource = style.StrokePaint().Resource()) {
EnsureResources(resources).SetStroke(
CastResource<LayoutSVGResourcePaintServer>(*stroke_resource));
}
}
if (auto* pattern = ToSVGPatternElementOrNull(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();
}
InvalidationModeMask SVGResources::RemoveClientFromCacheAffectingObjectBounds(
SVGResourceClient& client) const {
if (!clipper_filter_masker_data_)
return 0;
InvalidationModeMask invalidation_flags = 0;
if (LayoutSVGResourceClipper* clipper = clipper_filter_masker_data_->clipper)
clipper->RemoveClientFromCache(client);
if (LayoutSVGResourceFilter* filter = clipper_filter_masker_data_->filter) {
if (filter->RemoveClientFromCache(client))
invalidation_flags |= SVGResourceClient::kPaintInvalidation;
}
if (LayoutSVGResourceMasker* masker = clipper_filter_masker_data_->masker)
masker->RemoveClientFromCache(client);
return invalidation_flags | SVGResourceClient::kBoundariesInvalidation;
}
InvalidationModeMask SVGResources::RemoveClientFromCache(
SVGResourceClient& client) const {
if (!HasResourceData())
return 0;
if (linked_resource_) {
DCHECK(!clipper_filter_masker_data_);
DCHECK(!marker_data_);
DCHECK(!fill_stroke_data_);
linked_resource_->RemoveClientFromCache(client);
// The only linked resources are gradients and patterns, i.e
// always a paint server.
return SVGResourceClient::kPaintInvalidation;
}
InvalidationModeMask invalidation_flags =
RemoveClientFromCacheAffectingObjectBounds(client);
if (marker_data_) {
if (LayoutSVGResourceMarker* marker = marker_data_->marker_start)
marker->RemoveClientFromCache(client);
if (LayoutSVGResourceMarker* marker = marker_data_->marker_mid)
marker->RemoveClientFromCache(client);
if (LayoutSVGResourceMarker* marker = marker_data_->marker_end)
marker->RemoveClientFromCache(client);
invalidation_flags |= SVGResourceClient::kBoundariesInvalidation;
}
if (fill_stroke_data_) {
if (LayoutSVGResourcePaintServer* fill = fill_stroke_data_->fill)
fill->RemoveClientFromCache(client);
if (LayoutSVGResourcePaintServer* stroke = fill_stroke_data_->stroke)
stroke->RemoveClientFromCache(client);
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_ = ClipperFilterMaskerData::Create();
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_ = ClipperFilterMaskerData::Create();
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_ = MarkerData::Create();
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_ = MarkerData::Create();
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_ = MarkerData::Create();
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_ = ClipperFilterMaskerData::Create();
clipper_filter_masker_data_->masker = masker;
}
void SVGResources::SetFill(LayoutSVGResourcePaintServer* fill) {
if (!fill)
return;
if (!fill_stroke_data_)
fill_stroke_data_ = FillStrokeData::Create();
fill_stroke_data_->fill = fill;
}
void SVGResources::SetStroke(LayoutSVGResourcePaintServer* stroke) {
if (!stroke)
return;
if (!fill_stroke_data_)
fill_stroke_data_ = FillStrokeData::Create();
fill_stroke_data_->stroke = stroke;
}
void SVGResources::SetLinkedResource(
LayoutSVGResourceContainer* linked_resource) {
if (!linked_resource)
return;
linked_resource_ = linked_resource;
}
#ifndef NDEBUG
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().data());
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 =
ToReferenceClipPathOperationOrNull(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 =
ToReferenceClipPathOperationOrNull(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);
}
SVGElementResourceClient::SVGElementResourceClient(SVGElement* element)
: element_(element) {}
void SVGElementResourceClient::ResourceContentChanged(
InvalidationModeMask invalidation_mask) {
LayoutObject* layout_object = element_->GetLayoutObject();
if (!layout_object)
return;
bool mark_for_invalidation =
invalidation_mask & ~SVGResourceClient::kParentOnlyInvalidation;
if (layout_object->IsSVGResourceContainer()) {
ToLayoutSVGResourceContainer(layout_object)
->RemoveAllClientsFromCache(mark_for_invalidation);
return;
}
if (mark_for_invalidation) {
LayoutSVGResourceContainer::MarkClientForInvalidation(*layout_object,
invalidation_mask);
}
// Special case for filter invalidation.
if (invalidation_mask & SVGResourceClient::kSkipAncestorInvalidation)
return;
bool needs_layout =
invalidation_mask & SVGResourceClient::kLayoutInvalidation;
LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation(
*layout_object, needs_layout);
}
void SVGElementResourceClient::ResourceElementChanged() {
if (LayoutObject* layout_object = element_->GetLayoutObject())
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::Trace(Visitor* visitor) {
visitor->Trace(element_);
SVGResourceClient::Trace(visitor);
}
} // namespace blink