/src/mozilla-central/dom/svg/SVGMPathElement.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include "mozilla/ArrayUtils.h" |
8 | | |
9 | | #include "mozilla/dom/SVGMPathElement.h" |
10 | | #include "nsDebug.h" |
11 | | #include "mozilla/dom/SVGAnimateMotionElement.h" |
12 | | #include "mozilla/dom/SVGPathElement.h" |
13 | | #include "nsContentUtils.h" |
14 | | #include "mozilla/dom/SVGMPathElementBinding.h" |
15 | | #include "nsIURI.h" |
16 | | |
17 | | NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(MPath) |
18 | | |
19 | | namespace mozilla { |
20 | | namespace dom { |
21 | | |
22 | | JSObject* |
23 | | SVGMPathElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) |
24 | 0 | { |
25 | 0 | return SVGMPathElement_Binding::Wrap(aCx, this, aGivenProto); |
26 | 0 | } |
27 | | |
28 | | nsSVGElement::StringInfo SVGMPathElement::sStringInfo[2] = |
29 | | { |
30 | | { &nsGkAtoms::href, kNameSpaceID_None, false }, |
31 | | { &nsGkAtoms::href, kNameSpaceID_XLink, false } |
32 | | }; |
33 | | |
34 | | // Cycle collection magic -- based on SVGUseElement |
35 | | NS_IMPL_CYCLE_COLLECTION_CLASS(SVGMPathElement) |
36 | | |
37 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGMPathElement, |
38 | 0 | SVGMPathElementBase) |
39 | 0 | tmp->UnlinkHrefTarget(false); |
40 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
41 | | |
42 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGMPathElement, |
43 | 0 | SVGMPathElementBase) |
44 | 0 | tmp->mPathTracker.Traverse(&cb); |
45 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
46 | | |
47 | | //---------------------------------------------------------------------- |
48 | | // nsISupports methods |
49 | | |
50 | | NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(SVGMPathElement, |
51 | | SVGMPathElementBase, |
52 | | nsIMutationObserver) |
53 | | |
54 | | // Constructor |
55 | | SVGMPathElement::SVGMPathElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) |
56 | | : SVGMPathElementBase(std::move(aNodeInfo)) |
57 | | , mPathTracker(this) |
58 | 0 | { |
59 | 0 | } |
60 | | |
61 | | SVGMPathElement::~SVGMPathElement() |
62 | 0 | { |
63 | 0 | UnlinkHrefTarget(false); |
64 | 0 | } |
65 | | |
66 | | //---------------------------------------------------------------------- |
67 | | // nsINode methods |
68 | | |
69 | | NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGMPathElement) |
70 | | |
71 | | already_AddRefed<SVGAnimatedString> |
72 | | SVGMPathElement::Href() |
73 | 0 | { |
74 | 0 | return mStringAttributes[HREF].IsExplicitlySet() |
75 | 0 | ? mStringAttributes[HREF].ToDOMAnimatedString(this) |
76 | 0 | : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this); |
77 | 0 | } |
78 | | |
79 | | //---------------------------------------------------------------------- |
80 | | // nsIContent methods |
81 | | |
82 | | nsresult |
83 | | SVGMPathElement::BindToTree(nsIDocument* aDocument, |
84 | | nsIContent* aParent, |
85 | | nsIContent* aBindingParent) |
86 | 0 | { |
87 | 0 | MOZ_ASSERT(!mPathTracker.get(), |
88 | 0 | "Shouldn't have href-target yet (or it should've been cleared)"); |
89 | 0 | nsresult rv = SVGMPathElementBase::BindToTree(aDocument, aParent, |
90 | 0 | aBindingParent); |
91 | 0 | NS_ENSURE_SUCCESS(rv,rv); |
92 | 0 |
|
93 | 0 | if (aDocument) { |
94 | 0 | const nsAttrValue* hrefAttrValue = |
95 | 0 | HasAttr(kNameSpaceID_None, nsGkAtoms::href) |
96 | 0 | ? mAttrs.GetAttr(nsGkAtoms::href, kNameSpaceID_None) |
97 | 0 | : mAttrs.GetAttr(nsGkAtoms::href, kNameSpaceID_XLink); |
98 | 0 | if (hrefAttrValue) { |
99 | 0 | UpdateHrefTarget(aParent, hrefAttrValue->GetStringValue()); |
100 | 0 | } |
101 | 0 | } |
102 | 0 |
|
103 | 0 | return NS_OK; |
104 | 0 | } |
105 | | |
106 | | void |
107 | | SVGMPathElement::UnbindFromTree(bool aDeep, bool aNullParent) |
108 | 0 | { |
109 | 0 | UnlinkHrefTarget(true); |
110 | 0 | SVGMPathElementBase::UnbindFromTree(aDeep, aNullParent); |
111 | 0 | } |
112 | | |
113 | | bool |
114 | | SVGMPathElement::ParseAttribute(int32_t aNamespaceID, |
115 | | nsAtom* aAttribute, |
116 | | const nsAString& aValue, |
117 | | nsIPrincipal* aMaybeScriptedPrincipal, |
118 | | nsAttrValue& aResult) |
119 | 0 | { |
120 | 0 | bool returnVal = |
121 | 0 | SVGMPathElementBase::ParseAttribute(aNamespaceID, aAttribute, |
122 | 0 | aValue, |
123 | 0 | aMaybeScriptedPrincipal, |
124 | 0 | aResult); |
125 | 0 | if ((aNamespaceID == kNameSpaceID_XLink || |
126 | 0 | aNamespaceID == kNameSpaceID_None ) && |
127 | 0 | aAttribute == nsGkAtoms::href && |
128 | 0 | IsInUncomposedDoc()) { |
129 | 0 | // Note: If we fail the IsInDoc call, it's ok -- we'll update the target |
130 | 0 | // on next BindToTree call. |
131 | 0 |
|
132 | 0 | // Note: "href" takes priority over xlink:href. So if "xlink:href" is being |
133 | 0 | // set here, we only let that update our target if "href" is *unset*. |
134 | 0 | if (aNamespaceID != kNameSpaceID_XLink || |
135 | 0 | !mStringAttributes[HREF].IsExplicitlySet()) { |
136 | 0 | UpdateHrefTarget(GetParent(), aValue); |
137 | 0 | } |
138 | 0 | } |
139 | 0 | return returnVal; |
140 | 0 | } |
141 | | |
142 | | nsresult |
143 | | SVGMPathElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, |
144 | | const nsAttrValue* aValue, |
145 | | const nsAttrValue* aOldValue, |
146 | | nsIPrincipal* aMaybeScriptedPrincipal, |
147 | | bool aNotify) |
148 | 0 | { |
149 | 0 | if (!aValue && aName == nsGkAtoms::href) { |
150 | 0 | // href attr being removed. |
151 | 0 | if (aNamespaceID == kNameSpaceID_None) { |
152 | 0 | UnlinkHrefTarget(true); |
153 | 0 |
|
154 | 0 | // After unsetting href, we may still have xlink:href, so we should |
155 | 0 | // try to add it back. |
156 | 0 | const nsAttrValue* xlinkHref = |
157 | 0 | mAttrs.GetAttr(nsGkAtoms::href, kNameSpaceID_XLink); |
158 | 0 | if (xlinkHref) { |
159 | 0 | UpdateHrefTarget(GetParent(), xlinkHref->GetStringValue()); |
160 | 0 | } |
161 | 0 | } else if (aNamespaceID == kNameSpaceID_XLink && |
162 | 0 | !HasAttr(kNameSpaceID_None, nsGkAtoms::href)) { |
163 | 0 | UnlinkHrefTarget(true); |
164 | 0 | } // else: we unset some random-namespace href attribute, or unset xlink:href |
165 | 0 | // but still have href attribute, so keep the target linking to href. |
166 | 0 | } |
167 | 0 |
|
168 | 0 | return SVGMPathElementBase::AfterSetAttr(aNamespaceID, aName, |
169 | 0 | aValue, aOldValue, |
170 | 0 | aMaybeScriptedPrincipal, aNotify); |
171 | 0 | } |
172 | | |
173 | | //---------------------------------------------------------------------- |
174 | | // nsSVGElement methods |
175 | | |
176 | | nsSVGElement::StringAttributesInfo |
177 | | SVGMPathElement::GetStringInfo() |
178 | 0 | { |
179 | 0 | return StringAttributesInfo(mStringAttributes, sStringInfo, |
180 | 0 | ArrayLength(sStringInfo)); |
181 | 0 | } |
182 | | |
183 | | //---------------------------------------------------------------------- |
184 | | // nsIMutationObserver methods |
185 | | |
186 | | void |
187 | | SVGMPathElement::AttributeChanged(Element* aElement, |
188 | | int32_t aNameSpaceID, |
189 | | nsAtom* aAttribute, |
190 | | int32_t aModType, |
191 | | const nsAttrValue* aOldValue) |
192 | 0 | { |
193 | 0 | if (aNameSpaceID == kNameSpaceID_None) { |
194 | 0 | if (aAttribute == nsGkAtoms::d) { |
195 | 0 | NotifyParentOfMpathChange(GetParent()); |
196 | 0 | } |
197 | 0 | } |
198 | 0 | } |
199 | | |
200 | | //---------------------------------------------------------------------- |
201 | | // Public helper methods |
202 | | |
203 | | SVGPathElement* |
204 | | SVGMPathElement::GetReferencedPath() |
205 | 0 | { |
206 | 0 | if (!HasAttr(kNameSpaceID_XLink, nsGkAtoms::href) && |
207 | 0 | !HasAttr(kNameSpaceID_None, nsGkAtoms::href)) { |
208 | 0 | MOZ_ASSERT(!mPathTracker.get(), |
209 | 0 | "We shouldn't have a href target " |
210 | 0 | "if we don't have an xlink:href or href attribute"); |
211 | 0 | return nullptr; |
212 | 0 | } |
213 | 0 |
|
214 | 0 | nsIContent* genericTarget = mPathTracker.get(); |
215 | 0 | if (genericTarget && genericTarget->IsSVGElement(nsGkAtoms::path)) { |
216 | 0 | return static_cast<SVGPathElement*>(genericTarget); |
217 | 0 | } |
218 | 0 | return nullptr; |
219 | 0 | } |
220 | | |
221 | | //---------------------------------------------------------------------- |
222 | | // Protected helper methods |
223 | | |
224 | | void |
225 | | SVGMPathElement::UpdateHrefTarget(nsIContent* aParent, |
226 | | const nsAString& aHrefStr) |
227 | 0 | { |
228 | 0 | nsCOMPtr<nsIURI> targetURI; |
229 | 0 | nsCOMPtr<nsIURI> baseURI = GetBaseURI(); |
230 | 0 | nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), |
231 | 0 | aHrefStr, OwnerDoc(), baseURI); |
232 | 0 |
|
233 | 0 | // Stop observing old target (if any) |
234 | 0 | if (mPathTracker.get()) { |
235 | 0 | mPathTracker.get()->RemoveMutationObserver(this); |
236 | 0 | } |
237 | 0 |
|
238 | 0 | if (aParent) { |
239 | 0 | // Pass in |aParent| instead of |this| -- first argument is only used |
240 | 0 | // for a call to GetComposedDoc(), and |this| might not have a current |
241 | 0 | // document yet (if our caller is BindToTree). |
242 | 0 | // Bug 1415044 to investigate which referrer we should use |
243 | 0 | mPathTracker.Reset(aParent, targetURI, |
244 | 0 | OwnerDoc()->GetDocumentURI(), |
245 | 0 | OwnerDoc()->GetReferrerPolicy()); |
246 | 0 | } else { |
247 | 0 | // if we don't have a parent, then there's no animateMotion element |
248 | 0 | // depending on our target, so there's no point tracking it right now. |
249 | 0 | mPathTracker.Unlink(); |
250 | 0 | } |
251 | 0 |
|
252 | 0 | // Start observing new target (if any) |
253 | 0 | if (mPathTracker.get()) { |
254 | 0 | mPathTracker.get()->AddMutationObserver(this); |
255 | 0 | } |
256 | 0 |
|
257 | 0 | NotifyParentOfMpathChange(aParent); |
258 | 0 | } |
259 | | |
260 | | void |
261 | | SVGMPathElement::UnlinkHrefTarget(bool aNotifyParent) |
262 | 0 | { |
263 | 0 | // Stop observing old target (if any) |
264 | 0 | if (mPathTracker.get()) { |
265 | 0 | mPathTracker.get()->RemoveMutationObserver(this); |
266 | 0 | } |
267 | 0 | mPathTracker.Unlink(); |
268 | 0 |
|
269 | 0 | if (aNotifyParent) { |
270 | 0 | NotifyParentOfMpathChange(GetParent()); |
271 | 0 | } |
272 | 0 | } |
273 | | |
274 | | void |
275 | | SVGMPathElement::NotifyParentOfMpathChange(nsIContent* aParent) |
276 | 0 | { |
277 | 0 | if (aParent && aParent->IsSVGElement(nsGkAtoms::animateMotion)) { |
278 | 0 |
|
279 | 0 | SVGAnimateMotionElement* animateMotionParent = |
280 | 0 | static_cast<SVGAnimateMotionElement*>(aParent); |
281 | 0 |
|
282 | 0 | animateMotionParent->MpathChanged(); |
283 | 0 | AnimationNeedsResample(); |
284 | 0 | } |
285 | 0 | } |
286 | | |
287 | | } // namespace dom |
288 | | } // namespace mozilla |
289 | | |