Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/svg/SVGAElement.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/dom/SVGAElement.h"
8
9
#include "mozilla/Attributes.h"
10
#include "mozilla/EventDispatcher.h"
11
#include "mozilla/EventStates.h"
12
#include "mozilla/dom/SVGAElementBinding.h"
13
#include "nsCOMPtr.h"
14
#include "nsContentUtils.h"
15
#include "nsGkAtoms.h"
16
#include "nsSVGString.h"
17
#include "nsIContentInlines.h"
18
#include "nsIURI.h"
19
20
NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(A)
21
22
namespace mozilla {
23
namespace dom {
24
25
JSObject*
26
SVGAElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
27
0
{
28
0
  return SVGAElement_Binding::Wrap(aCx, this, aGivenProto);
29
0
}
30
31
nsSVGElement::StringInfo SVGAElement::sStringInfo[3] =
32
{
33
  { &nsGkAtoms::href, kNameSpaceID_None, true },
34
  { &nsGkAtoms::href, kNameSpaceID_XLink, true },
35
  { &nsGkAtoms::target, kNameSpaceID_None, true }
36
};
37
38
// static
39
const DOMTokenListSupportedToken SVGAElement::sSupportedRelValues[] = {
40
  "noreferrer",
41
  "noopener",
42
  nullptr
43
};
44
45
//----------------------------------------------------------------------
46
// nsISupports methods
47
48
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGAElement)
49
0
  NS_INTERFACE_MAP_ENTRY(Link)
50
0
NS_INTERFACE_MAP_END_INHERITING(SVGAElementBase)
51
52
NS_IMPL_CYCLE_COLLECTION_INHERITED(SVGAElement,
53
                                   SVGAElementBase,
54
                                   mRelList)
55
56
NS_IMPL_ADDREF_INHERITED(SVGAElement, SVGAElementBase)
57
NS_IMPL_RELEASE_INHERITED(SVGAElement, SVGAElementBase)
58
59
//----------------------------------------------------------------------
60
// Implementation
61
62
SVGAElement::SVGAElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
63
  : SVGAElementBase(std::move(aNodeInfo))
64
  , Link(this)
65
0
{
66
0
}
67
68
SVGAElement::~SVGAElement()
69
0
{
70
0
}
71
72
already_AddRefed<SVGAnimatedString>
73
SVGAElement::Href()
74
0
{
75
0
  return mStringAttributes[HREF].IsExplicitlySet()
76
0
         ? mStringAttributes[HREF].ToDOMAnimatedString(this)
77
0
         : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this);
78
0
}
79
80
//----------------------------------------------------------------------
81
// Link methods
82
83
bool
84
SVGAElement::ElementHasHref() const
85
0
{
86
0
  return mStringAttributes[HREF].IsExplicitlySet() ||
87
0
         mStringAttributes[XLINK_HREF].IsExplicitlySet();
88
0
}
89
90
//----------------------------------------------------------------------
91
// nsINode methods
92
93
void
94
SVGAElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
95
0
{
96
0
  Element::GetEventTargetParent(aVisitor);
97
0
98
0
  GetEventTargetParentForLinks(aVisitor);
99
0
}
100
101
nsresult
102
SVGAElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
103
0
{
104
0
  return PostHandleEventForLinks(aVisitor);
105
0
}
106
107
NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGAElement)
108
109
110
//----------------------------------------------------------------------
111
112
already_AddRefed<SVGAnimatedString>
113
SVGAElement::Target()
114
0
{
115
0
  return mStringAttributes[TARGET].ToDOMAnimatedString(this);
116
0
}
117
118
void
119
SVGAElement::GetDownload(nsAString& aDownload)
120
0
{
121
0
  GetAttr(nsGkAtoms::download, aDownload);
122
0
}
123
124
void
125
SVGAElement::SetDownload(const nsAString& aDownload, ErrorResult& rv)
126
0
{
127
0
  SetAttr(nsGkAtoms::download, aDownload, rv);
128
0
}
129
130
void
131
SVGAElement::GetPing(nsAString& aPing)
132
0
{
133
0
  GetAttr(nsGkAtoms::ping, aPing);
134
0
}
135
136
void
137
SVGAElement::SetPing(const nsAString& aPing, ErrorResult& rv)
138
0
{
139
0
  SetAttr(nsGkAtoms::ping, aPing, rv);
140
0
}
141
142
void
143
SVGAElement::GetRel(nsAString& aRel)
144
0
{
145
0
  GetAttr(nsGkAtoms::rel, aRel);
146
0
}
147
148
void
149
SVGAElement::SetRel(const nsAString& aRel, ErrorResult& rv)
150
0
{
151
0
  SetAttr(nsGkAtoms::rel, aRel, rv);
152
0
}
153
154
void
155
SVGAElement::GetReferrerPolicy(nsAString& aPolicy)
156
0
{
157
0
  GetEnumAttr(nsGkAtoms::referrerpolicy, EmptyCString().get(), aPolicy);
158
0
}
159
160
void
161
SVGAElement::SetReferrerPolicy(const nsAString& aPolicy,
162
                               mozilla::ErrorResult& rv)
163
0
{
164
0
  SetAttr(nsGkAtoms::referrerpolicy, aPolicy, rv);
165
0
}
166
167
nsDOMTokenList*
168
SVGAElement:: RelList()
169
0
{
170
0
  if (!mRelList) {
171
0
    mRelList = new nsDOMTokenList(this, nsGkAtoms::rel, sSupportedRelValues);
172
0
  }
173
0
  return mRelList;
174
0
}
175
176
void
177
SVGAElement::GetHreflang(nsAString& aHreflang)
178
0
{
179
0
  GetAttr(nsGkAtoms::hreflang, aHreflang);
180
0
}
181
182
void
183
SVGAElement::SetHreflang(const nsAString& aHreflang, mozilla::ErrorResult& rv)
184
0
{
185
0
  SetAttr(nsGkAtoms::hreflang, aHreflang, rv);
186
0
}
187
188
void SVGAElement::GetType(nsAString& aType)
189
0
{
190
0
  GetAttr(nsGkAtoms::type, aType);
191
0
}
192
193
void SVGAElement::SetType(const nsAString& aType, mozilla::ErrorResult& rv)
194
0
{
195
0
  SetAttr(nsGkAtoms::type, aType, rv);
196
0
}
197
198
void SVGAElement::GetText(nsAString& aText, mozilla::ErrorResult& rv)
199
0
{
200
0
  if (NS_WARN_IF(!nsContentUtils::GetNodeTextContent(this, true, aText, fallible))) {
201
0
    rv.Throw(NS_ERROR_OUT_OF_MEMORY);
202
0
  }
203
0
}
204
205
void SVGAElement::SetText(const nsAString& aText, mozilla::ErrorResult& rv)
206
0
{
207
0
  rv = nsContentUtils::SetNodeTextContent(this, aText, false);
208
0
}
209
210
//----------------------------------------------------------------------
211
// nsIContent methods
212
213
nsresult
214
SVGAElement::BindToTree(nsIDocument *aDocument, nsIContent *aParent,
215
                        nsIContent *aBindingParent)
216
0
{
217
0
  Link::ResetLinkState(false, Link::ElementHasHref());
218
0
219
0
  nsresult rv = SVGAElementBase::BindToTree(aDocument, aParent,
220
0
                                            aBindingParent);
221
0
  NS_ENSURE_SUCCESS(rv, rv);
222
0
223
0
  nsIDocument* doc = GetComposedDoc();
224
0
  if (doc) {
225
0
    doc->RegisterPendingLinkUpdate(this);
226
0
  }
227
0
228
0
  return NS_OK;
229
0
}
230
231
void
232
SVGAElement::UnbindFromTree(bool aDeep, bool aNullParent)
233
0
{
234
0
  // If this link is ever reinserted into a document, it might
235
0
  // be under a different xml:base, so forget the cached state now.
236
0
  Link::ResetLinkState(false, Link::ElementHasHref());
237
0
238
0
  SVGAElementBase::UnbindFromTree(aDeep, aNullParent);
239
0
}
240
241
already_AddRefed<nsIURI>
242
SVGAElement::GetHrefURI() const
243
0
{
244
0
  nsCOMPtr<nsIURI> hrefURI;
245
0
  return IsLink(getter_AddRefs(hrefURI)) ? hrefURI.forget() : nullptr;
246
0
}
247
248
249
NS_IMETHODIMP_(bool)
250
SVGAElement::IsAttributeMapped(const nsAtom* name) const
251
0
{
252
0
  static const MappedAttributeEntry* const map[] = {
253
0
    sFEFloodMap,
254
0
    sFiltersMap,
255
0
    sFontSpecificationMap,
256
0
    sGradientStopMap,
257
0
    sLightingEffectsMap,
258
0
    sMarkersMap,
259
0
    sTextContentElementsMap,
260
0
    sViewportsMap
261
0
  };
262
0
263
0
  return FindAttributeDependence(name, map) ||
264
0
    SVGAElementBase::IsAttributeMapped(name);
265
0
}
266
267
int32_t
268
SVGAElement::TabIndexDefault()
269
0
{
270
0
  return 0;
271
0
}
272
273
static bool
274
IsNodeInEditableRegion(nsINode* aNode)
275
0
{
276
0
  while (aNode) {
277
0
    if (aNode->IsEditable()) {
278
0
      return true;
279
0
    }
280
0
    aNode = aNode->GetParent();
281
0
  }
282
0
  return false;
283
0
}
284
285
bool
286
SVGAElement::IsSVGFocusable(bool* aIsFocusable, int32_t* aTabIndex)
287
0
{
288
0
  if (nsSVGElement::IsSVGFocusable(aIsFocusable, aTabIndex)) {
289
0
    return true;
290
0
  }
291
0
292
0
  // cannot focus links if there is no link handler
293
0
  nsIDocument* doc = GetComposedDoc();
294
0
  if (doc) {
295
0
    nsPresContext* presContext = doc->GetPresContext();
296
0
    if (presContext && !presContext->GetLinkHandler()) {
297
0
      *aIsFocusable = false;
298
0
      return false;
299
0
    }
300
0
  }
301
0
302
0
  // Links that are in an editable region should never be focusable, even if
303
0
  // they are in a contenteditable="false" region.
304
0
  if (IsNodeInEditableRegion(this)) {
305
0
    if (aTabIndex) {
306
0
      *aTabIndex = -1;
307
0
    }
308
0
309
0
    *aIsFocusable = false;
310
0
311
0
    return true;
312
0
  }
313
0
314
0
  if (!HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
315
0
    // check whether we're actually a link
316
0
    if (!Link::HasURI()) {
317
0
      // Not tabbable or focusable without href (bug 17605), unless
318
0
      // forced to be via presence of nonnegative tabindex attribute
319
0
      if (aTabIndex) {
320
0
        *aTabIndex = -1;
321
0
      }
322
0
323
0
      *aIsFocusable = false;
324
0
325
0
      return false;
326
0
    }
327
0
  }
328
0
329
0
  if (aTabIndex && (sTabFocusModel & eTabFocus_linksMask) == 0) {
330
0
    *aTabIndex = -1;
331
0
  }
332
0
333
0
  *aIsFocusable = true;
334
0
335
0
  return false;
336
0
}
337
338
bool
339
SVGAElement::IsLink(nsIURI** aURI) const
340
0
{
341
0
  // To be a clickable XLink for styling and interaction purposes, we require:
342
0
  //
343
0
  //   xlink:href    - must be set
344
0
  //   xlink:type    - must be unset or set to "" or set to "simple"
345
0
  //   xlink:show    - must be unset or set to "", "new" or "replace"
346
0
  //   xlink:actuate - must be unset or set to "" or "onRequest"
347
0
  //
348
0
  // For any other values, we're either not a *clickable* XLink, or the end
349
0
  // result is poorly specified. Either way, we return false.
350
0
351
0
  static Element::AttrValuesArray sTypeVals[] =
352
0
    { &nsGkAtoms::_empty, &nsGkAtoms::simple, nullptr };
353
0
354
0
  static Element::AttrValuesArray sShowVals[] =
355
0
    { &nsGkAtoms::_empty, &nsGkAtoms::_new, &nsGkAtoms::replace, nullptr };
356
0
357
0
  static Element::AttrValuesArray sActuateVals[] =
358
0
    { &nsGkAtoms::_empty, &nsGkAtoms::onRequest, nullptr };
359
0
360
0
  // Optimization: check for href first for early return
361
0
  bool useBareHref = mStringAttributes[HREF].IsExplicitlySet();
362
0
363
0
  if ((useBareHref || mStringAttributes[XLINK_HREF].IsExplicitlySet()) &&
364
0
      FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::type,
365
0
                      sTypeVals, eCaseMatters) !=
366
0
                      Element::ATTR_VALUE_NO_MATCH &&
367
0
      FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show,
368
0
                      sShowVals, eCaseMatters) !=
369
0
                      Element::ATTR_VALUE_NO_MATCH &&
370
0
      FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::actuate,
371
0
                      sActuateVals, eCaseMatters) !=
372
0
                      Element::ATTR_VALUE_NO_MATCH) {
373
0
    nsCOMPtr<nsIURI> baseURI = GetBaseURI();
374
0
    // Get absolute URI
375
0
    nsAutoString str;
376
0
    const uint8_t idx = useBareHref ? HREF : XLINK_HREF;
377
0
    mStringAttributes[idx].GetAnimValue(str, this);
378
0
    nsContentUtils::NewURIWithDocumentCharset(aURI, str, OwnerDoc(), baseURI);
379
0
    // must promise out param is non-null if we return true
380
0
    return !!*aURI;
381
0
  }
382
0
383
0
  *aURI = nullptr;
384
0
  return false;
385
0
}
386
387
void
388
SVGAElement::GetLinkTarget(nsAString& aTarget)
389
0
{
390
0
  mStringAttributes[TARGET].GetAnimValue(aTarget, this);
391
0
  if (aTarget.IsEmpty()) {
392
0
393
0
    static Element::AttrValuesArray sShowVals[] =
394
0
      { &nsGkAtoms::_new, &nsGkAtoms::replace, nullptr };
395
0
396
0
    switch (FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show,
397
0
                            sShowVals, eCaseMatters)) {
398
0
    case 0:
399
0
      aTarget.AssignLiteral("_blank");
400
0
      return;
401
0
    case 1:
402
0
      return;
403
0
    }
404
0
    nsIDocument* ownerDoc = OwnerDoc();
405
0
    if (ownerDoc) {
406
0
      ownerDoc->GetBaseTarget(aTarget);
407
0
    }
408
0
  }
409
0
}
410
411
EventStates
412
SVGAElement::IntrinsicState() const
413
0
{
414
0
  return Link::LinkState() | SVGAElementBase::IntrinsicState();
415
0
}
416
417
nsresult
418
SVGAElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
419
                          const nsAttrValue* aValue,
420
                          const nsAttrValue* aOldValue,
421
                          nsIPrincipal* aMaybeScriptedPrincipal,
422
                          bool aNotify)
423
0
{
424
0
  if (aName == nsGkAtoms::href &&
425
0
      (aNameSpaceID == kNameSpaceID_XLink ||
426
0
       aNameSpaceID == kNameSpaceID_None)) {
427
0
    // We can't assume that null aValue means we no longer have an href, because
428
0
    // we could be unsetting xlink:href but still have a null-namespace href, or
429
0
    // vice versa.  But we can fast-path the case when we _do_ have a new value.
430
0
    Link::ResetLinkState(aNotify, aValue || Link::ElementHasHref());
431
0
  }
432
0
433
0
  return SVGAElementBase::AfterSetAttr(aNameSpaceID, aName, aValue, aOldValue,
434
0
                                       aMaybeScriptedPrincipal, aNotify);
435
0
}
436
437
//----------------------------------------------------------------------
438
// nsSVGElement methods
439
440
nsSVGElement::StringAttributesInfo
441
SVGAElement::GetStringInfo()
442
0
{
443
0
  return StringAttributesInfo(mStringAttributes, sStringInfo,
444
0
                              ArrayLength(sStringInfo));
445
0
}
446
447
} // namespace dom
448
} // namespace mozilla