layout/svg/nsSVGFilterFrame.cpp

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:74e42c124e84
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 // Main header first:
7 #include "nsSVGFilterFrame.h"
8
9 // Keep others in (case-insensitive) order:
10 #include "gfxUtils.h"
11 #include "nsGkAtoms.h"
12 #include "nsRenderingContext.h"
13 #include "nsSVGEffects.h"
14 #include "nsSVGElement.h"
15 #include "mozilla/dom/SVGFilterElement.h"
16 #include "nsSVGFilterInstance.h"
17 #include "nsSVGFilterPaintCallback.h"
18 #include "nsSVGIntegrationUtils.h"
19 #include "nsSVGUtils.h"
20 #include "nsContentUtils.h"
21
22 using namespace mozilla::dom;
23
24 nsIFrame*
25 NS_NewSVGFilterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
26 {
27 return new (aPresShell) nsSVGFilterFrame(aContext);
28 }
29
30 NS_IMPL_FRAMEARENA_HELPERS(nsSVGFilterFrame)
31
32 class MOZ_STACK_CLASS nsSVGFilterFrame::AutoFilterReferencer
33 {
34 public:
35 AutoFilterReferencer(nsSVGFilterFrame *aFrame MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
36 : mFrame(aFrame)
37 {
38 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
39 // Reference loops should normally be detected in advance and handled, so
40 // we're not expecting to encounter them here
41 NS_ABORT_IF_FALSE(!mFrame->mLoopFlag, "Undetected reference loop!");
42 mFrame->mLoopFlag = true;
43 }
44 ~AutoFilterReferencer() {
45 mFrame->mLoopFlag = false;
46 }
47 private:
48 nsSVGFilterFrame *mFrame;
49 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
50 };
51
52 uint16_t
53 nsSVGFilterFrame::GetEnumValue(uint32_t aIndex, nsIContent *aDefault)
54 {
55 nsSVGEnum& thisEnum =
56 static_cast<SVGFilterElement *>(mContent)->mEnumAttributes[aIndex];
57
58 if (thisEnum.IsExplicitlySet())
59 return thisEnum.GetAnimValue();
60
61 AutoFilterReferencer filterRef(this);
62
63 nsSVGFilterFrame *next = GetReferencedFilterIfNotInUse();
64 return next ? next->GetEnumValue(aIndex, aDefault) :
65 static_cast<SVGFilterElement *>(aDefault)->
66 mEnumAttributes[aIndex].GetAnimValue();
67 }
68
69 const nsSVGLength2 *
70 nsSVGFilterFrame::GetLengthValue(uint32_t aIndex, nsIContent *aDefault)
71 {
72 const nsSVGLength2 *thisLength =
73 &static_cast<SVGFilterElement *>(mContent)->mLengthAttributes[aIndex];
74
75 if (thisLength->IsExplicitlySet())
76 return thisLength;
77
78 AutoFilterReferencer filterRef(this);
79
80 nsSVGFilterFrame *next = GetReferencedFilterIfNotInUse();
81 return next ? next->GetLengthValue(aIndex, aDefault) :
82 &static_cast<SVGFilterElement *>(aDefault)->mLengthAttributes[aIndex];
83 }
84
85 const SVGFilterElement *
86 nsSVGFilterFrame::GetFilterContent(nsIContent *aDefault)
87 {
88 for (nsIContent* child = mContent->GetFirstChild();
89 child;
90 child = child->GetNextSibling()) {
91 nsRefPtr<nsSVGFE> primitive;
92 CallQueryInterface(child, (nsSVGFE**)getter_AddRefs(primitive));
93 if (primitive) {
94 return static_cast<SVGFilterElement *>(mContent);
95 }
96 }
97
98 AutoFilterReferencer filterRef(this);
99
100 nsSVGFilterFrame *next = GetReferencedFilterIfNotInUse();
101 return next ? next->GetFilterContent(aDefault) :
102 static_cast<SVGFilterElement *>(aDefault);
103 }
104
105 nsSVGFilterFrame *
106 nsSVGFilterFrame::GetReferencedFilter()
107 {
108 if (mNoHRefURI)
109 return nullptr;
110
111 nsSVGPaintingProperty *property = static_cast<nsSVGPaintingProperty*>
112 (Properties().Get(nsSVGEffects::HrefProperty()));
113
114 if (!property) {
115 // Fetch our Filter element's xlink:href attribute
116 SVGFilterElement *filter = static_cast<SVGFilterElement *>(mContent);
117 nsAutoString href;
118 filter->mStringAttributes[SVGFilterElement::HREF].GetAnimValue(href, filter);
119 if (href.IsEmpty()) {
120 mNoHRefURI = true;
121 return nullptr; // no URL
122 }
123
124 // Convert href to an nsIURI
125 nsCOMPtr<nsIURI> targetURI;
126 nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
127 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
128 mContent->GetCurrentDoc(), base);
129
130 property =
131 nsSVGEffects::GetPaintingProperty(targetURI, this, nsSVGEffects::HrefProperty());
132 if (!property)
133 return nullptr;
134 }
135
136 nsIFrame *result = property->GetReferencedFrame();
137 if (!result)
138 return nullptr;
139
140 nsIAtom* frameType = result->GetType();
141 if (frameType != nsGkAtoms::svgFilterFrame)
142 return nullptr;
143
144 return static_cast<nsSVGFilterFrame*>(result);
145 }
146
147 nsSVGFilterFrame *
148 nsSVGFilterFrame::GetReferencedFilterIfNotInUse()
149 {
150 nsSVGFilterFrame *referenced = GetReferencedFilter();
151 if (!referenced)
152 return nullptr;
153
154 if (referenced->mLoopFlag) {
155 // XXXjwatt: we should really send an error to the JavaScript Console here:
156 NS_WARNING("Filter reference loop detected while inheriting attribute!");
157 return nullptr;
158 }
159
160 return referenced;
161 }
162
163 nsresult
164 nsSVGFilterFrame::AttributeChanged(int32_t aNameSpaceID,
165 nsIAtom* aAttribute,
166 int32_t aModType)
167 {
168 if (aNameSpaceID == kNameSpaceID_None &&
169 (aAttribute == nsGkAtoms::x ||
170 aAttribute == nsGkAtoms::y ||
171 aAttribute == nsGkAtoms::width ||
172 aAttribute == nsGkAtoms::height ||
173 aAttribute == nsGkAtoms::filterUnits ||
174 aAttribute == nsGkAtoms::primitiveUnits)) {
175 nsSVGEffects::InvalidateDirectRenderingObservers(this);
176 } else if (aNameSpaceID == kNameSpaceID_XLink &&
177 aAttribute == nsGkAtoms::href) {
178 // Blow away our reference, if any
179 Properties().Delete(nsSVGEffects::HrefProperty());
180 mNoHRefURI = false;
181 // And update whoever references us
182 nsSVGEffects::InvalidateDirectRenderingObservers(this);
183 }
184 return nsSVGFilterFrameBase::AttributeChanged(aNameSpaceID,
185 aAttribute, aModType);
186 }
187
188 #ifdef DEBUG
189 void
190 nsSVGFilterFrame::Init(nsIContent* aContent,
191 nsIFrame* aParent,
192 nsIFrame* aPrevInFlow)
193 {
194 NS_ASSERTION(aContent->IsSVG(nsGkAtoms::filter),
195 "Content is not an SVG filter");
196
197 nsSVGFilterFrameBase::Init(aContent, aParent, aPrevInFlow);
198 }
199 #endif /* DEBUG */
200
201 nsIAtom *
202 nsSVGFilterFrame::GetType() const
203 {
204 return nsGkAtoms::svgFilterFrame;
205 }

mercurial