SVGFilterFrame.cpp (6382B)
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/. */ 6 7 // Main header first: 8 #include "SVGFilterFrame.h" 9 10 // Keep others in (case-insensitive) order: 11 #include "AutoReferenceChainGuard.h" 12 #include "SVGElement.h" 13 #include "SVGFilterInstance.h" 14 #include "SVGObserverUtils.h" 15 #include "gfxUtils.h" 16 #include "mozilla/PresShell.h" 17 #include "mozilla/dom/SVGFilterElement.h" 18 #include "nsContentUtils.h" 19 #include "nsGkAtoms.h" 20 21 using namespace mozilla; 22 using namespace mozilla::dom; 23 24 nsIFrame* NS_NewSVGFilterFrame(mozilla::PresShell* aPresShell, 25 mozilla::ComputedStyle* aStyle) { 26 return new (aPresShell) 27 mozilla::SVGFilterFrame(aStyle, aPresShell->GetPresContext()); 28 } 29 30 namespace mozilla { 31 32 NS_IMPL_FRAMEARENA_HELPERS(SVGFilterFrame) 33 34 uint16_t SVGFilterFrame::GetEnumValue(uint32_t aIndex, nsIContent* aDefault) { 35 SVGAnimatedEnumeration& thisEnum = 36 static_cast<SVGFilterElement*>(GetContent())->mEnumAttributes[aIndex]; 37 38 if (thisEnum.IsExplicitlySet()) { 39 return thisEnum.GetAnimValue(); 40 } 41 42 // Before we recurse, make sure we'll break reference loops and over long 43 // reference chains: 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] 51 .GetAnimValue(); 52 } 53 54 SVGFilterFrame* next = GetReferencedFilter(); 55 56 return next ? next->GetEnumValue(aIndex, aDefault) 57 : static_cast<SVGFilterElement*>(aDefault) 58 ->mEnumAttributes[aIndex] 59 .GetAnimValue(); 60 } 61 62 const SVGAnimatedLength* SVGFilterFrame::GetLengthValue(uint32_t aIndex, 63 nsIContent* aDefault) { 64 const SVGAnimatedLength* thisLength = 65 &static_cast<SVGFilterElement*>(GetContent())->mLengthAttributes[aIndex]; 66 67 if (thisLength->IsExplicitlySet()) { 68 return thisLength; 69 } 70 71 // Before we recurse, make sure we'll break reference loops and over long 72 // reference chains: 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]; 79 } 80 81 SVGFilterFrame* next = GetReferencedFilter(); 82 83 return next ? next->GetLengthValue(aIndex, aDefault) 84 : &static_cast<SVGFilterElement*>(aDefault) 85 ->mLengthAttributes[aIndex]; 86 } 87 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()); 93 } 94 } 95 96 // Before we recurse, make sure we'll break reference loops and over long 97 // reference chains: 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); 104 } 105 106 SVGFilterFrame* next = GetReferencedFilter(); 107 108 return next ? next->GetFilterContent(aDefault) 109 : static_cast<SVGFilterElement*>(aDefault); 110 } 111 112 SVGFilterFrame* SVGFilterFrame::GetReferencedFilter() { 113 if (mNoHRefURI) { 114 return nullptr; 115 } 116 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, 121 filter); 122 } else { 123 filter->mStringAttributes[SVGFilterElement::XLINK_HREF].GetAnimValue( 124 aHref, filter); 125 } 126 this->mNoHRefURI = aHref.IsEmpty(); 127 }; 128 129 nsIFrame* tframe = SVGObserverUtils::GetAndObserveTemplate(this, GetHref); 130 if (tframe && tframe->IsSVGFilterFrame()) { 131 return static_cast<SVGFilterFrame*>(tframe); 132 } 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. 136 137 return nullptr; 138 } 139 140 nsresult SVGFilterFrame::AttributeChanged(int32_t aNameSpaceID, 141 nsAtom* aAttribute, 142 AttrModType aModType) { 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); 154 mNoHRefURI = false; 155 // And update whoever references us 156 SVGObserverUtils::InvalidateRenderingObservers(this); 157 } 158 return SVGContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, 159 aModType); 160 } 161 162 #ifdef DEBUG 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"); 167 168 SVGContainerFrame::Init(aContent, aParent, aPrevInFlow); 169 } 170 #endif /* DEBUG */ 171 172 } // namespace mozilla