1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "SVGFilterFrame.h"
10 // Keep others in (case-insensitive) order:
11 #include "AutoReferenceChainGuard.h"
13 #include "mozilla/PresShell.h"
14 #include "mozilla/dom/SVGFilterElement.h"
15 #include "nsGkAtoms.h"
16 #include "SVGObserverUtils.h"
17 #include "SVGElement.h"
18 #include "SVGFilterInstance.h"
19 #include "nsContentUtils.h"
21 using namespace mozilla
;
22 using namespace mozilla::dom
;
24 nsIFrame
* NS_NewSVGFilterFrame(mozilla::PresShell
* aPresShell
,
25 mozilla::ComputedStyle
* aStyle
) {
26 return new (aPresShell
)
27 mozilla::SVGFilterFrame(aStyle
, aPresShell
->GetPresContext());
32 NS_IMPL_FRAMEARENA_HELPERS(SVGFilterFrame
)
34 uint16_t SVGFilterFrame::GetEnumValue(uint32_t aIndex
, nsIContent
* aDefault
) {
35 SVGAnimatedEnumeration
& thisEnum
=
36 static_cast<SVGFilterElement
*>(GetContent())->mEnumAttributes
[aIndex
];
38 if (thisEnum
.IsExplicitlySet()) {
39 return thisEnum
.GetAnimValue();
42 // Before we recurse, make sure we'll break reference loops and over long
44 static int16_t sRefChainLengthCounter
= AutoReferenceChainGuard::noChain
;
45 AutoReferenceChainGuard
refChainGuard(this, &mLoopFlag
,
46 &sRefChainLengthCounter
);
47 if (MOZ_UNLIKELY(!refChainGuard
.Reference())) {
48 // Break reference chain
49 return static_cast<SVGFilterElement
*>(aDefault
)
50 ->mEnumAttributes
[aIndex
]
54 SVGFilterFrame
* next
= GetReferencedFilter();
56 return next
? next
->GetEnumValue(aIndex
, aDefault
)
57 : static_cast<SVGFilterElement
*>(aDefault
)
58 ->mEnumAttributes
[aIndex
]
62 const SVGAnimatedLength
* SVGFilterFrame::GetLengthValue(uint32_t aIndex
,
63 nsIContent
* aDefault
) {
64 const SVGAnimatedLength
* thisLength
=
65 &static_cast<SVGFilterElement
*>(GetContent())->mLengthAttributes
[aIndex
];
67 if (thisLength
->IsExplicitlySet()) {
71 // Before we recurse, make sure we'll break reference loops and over long
73 static int16_t sRefChainLengthCounter
= AutoReferenceChainGuard::noChain
;
74 AutoReferenceChainGuard
refChainGuard(this, &mLoopFlag
,
75 &sRefChainLengthCounter
);
76 if (MOZ_UNLIKELY(!refChainGuard
.Reference())) {
77 // Break reference chain
78 return &static_cast<SVGFilterElement
*>(aDefault
)->mLengthAttributes
[aIndex
];
81 SVGFilterFrame
* next
= GetReferencedFilter();
83 return next
? next
->GetLengthValue(aIndex
, aDefault
)
84 : &static_cast<SVGFilterElement
*>(aDefault
)
85 ->mLengthAttributes
[aIndex
];
88 const SVGFilterElement
* SVGFilterFrame::GetFilterContent(nsIContent
* aDefault
) {
89 for (nsIContent
* child
= mContent
->GetFirstChild(); child
;
90 child
= child
->GetNextSibling()) {
91 if (child
->IsSVGFilterPrimitiveElement()) {
92 return static_cast<SVGFilterElement
*>(GetContent());
96 // Before we recurse, make sure we'll break reference loops and over long
98 static int16_t sRefChainLengthCounter
= AutoReferenceChainGuard::noChain
;
99 AutoReferenceChainGuard
refChainGuard(this, &mLoopFlag
,
100 &sRefChainLengthCounter
);
101 if (MOZ_UNLIKELY(!refChainGuard
.Reference())) {
102 // Break reference chain
103 return static_cast<SVGFilterElement
*>(aDefault
);
106 SVGFilterFrame
* next
= GetReferencedFilter();
108 return next
? next
->GetFilterContent(aDefault
)
109 : static_cast<SVGFilterElement
*>(aDefault
);
112 SVGFilterFrame
* SVGFilterFrame::GetReferencedFilter() {
117 auto GetHref
= [this](nsAString
& aHref
) {
118 SVGFilterElement
* filter
= static_cast<SVGFilterElement
*>(GetContent());
119 if (filter
->mStringAttributes
[SVGFilterElement::HREF
].IsExplicitlySet()) {
120 filter
->mStringAttributes
[SVGFilterElement::HREF
].GetAnimValue(aHref
,
123 filter
->mStringAttributes
[SVGFilterElement::XLINK_HREF
].GetAnimValue(
126 this->mNoHRefURI
= aHref
.IsEmpty();
129 nsIFrame
* tframe
= SVGObserverUtils::GetAndObserveTemplate(this, GetHref
);
130 if (tframe
&& tframe
->IsSVGFilterFrame()) {
131 return static_cast<SVGFilterFrame
*>(tframe
);
133 // We don't call SVGObserverUtils::RemoveTemplateObserver and set
134 // `mNoHRefURI = false` here since we want to be invalidated if the ID
135 // specified by our href starts resolving to a different/valid element.
140 nsresult
SVGFilterFrame::AttributeChanged(int32_t aNameSpaceID
,
143 if (aNameSpaceID
== kNameSpaceID_None
&&
144 (aAttribute
== nsGkAtoms::x
|| aAttribute
== nsGkAtoms::y
||
145 aAttribute
== nsGkAtoms::width
|| aAttribute
== nsGkAtoms::height
||
146 aAttribute
== nsGkAtoms::filterUnits
||
147 aAttribute
== nsGkAtoms::primitiveUnits
)) {
148 SVGObserverUtils::InvalidateRenderingObservers(this);
149 } else if ((aNameSpaceID
== kNameSpaceID_XLink
||
150 aNameSpaceID
== kNameSpaceID_None
) &&
151 aAttribute
== nsGkAtoms::href
) {
152 // Blow away our reference, if any
153 SVGObserverUtils::RemoveTemplateObserver(this);
155 // And update whoever references us
156 SVGObserverUtils::InvalidateRenderingObservers(this);
158 return SVGContainerFrame::AttributeChanged(aNameSpaceID
, aAttribute
,
163 void SVGFilterFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
164 nsIFrame
* aPrevInFlow
) {
165 NS_ASSERTION(aContent
->IsSVGElement(nsGkAtoms::filter
),
166 "Content is not an SVG filter");
168 SVGContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
172 } // namespace mozilla