Coverage Report

Created: 2018-09-25 14:53

/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