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/. */
7 #include "mozilla/dom/SVGAElement.h"
9 #include "mozilla/Attributes.h"
10 #include "mozilla/EventDispatcher.h"
11 #include "mozilla/EventStates.h"
12 #include "mozilla/dom/BindContext.h"
13 #include "mozilla/dom/DocumentInlines.h"
14 #include "mozilla/dom/SVGAElementBinding.h"
16 #include "nsContentUtils.h"
17 #include "nsGkAtoms.h"
18 #include "nsIContentInlines.h"
21 NS_IMPL_NS_NEW_SVG_ELEMENT(A
)
26 JSObject
* SVGAElement::WrapNode(JSContext
* aCx
,
27 JS::Handle
<JSObject
*> aGivenProto
) {
28 return SVGAElement_Binding::Wrap(aCx
, this, aGivenProto
);
31 SVGElement::StringInfo
SVGAElement::sStringInfo
[3] = {
32 {nsGkAtoms::href
, kNameSpaceID_None
, true},
33 {nsGkAtoms::href
, kNameSpaceID_XLink
, true},
34 {nsGkAtoms::target
, kNameSpaceID_None
, true}};
37 const DOMTokenListSupportedToken
SVGAElement::sSupportedRelValues
[] = {
38 "noreferrer", "noopener", nullptr};
40 //----------------------------------------------------------------------
41 // nsISupports methods
43 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGAElement
)
44 NS_INTERFACE_MAP_ENTRY(Link
)
45 NS_INTERFACE_MAP_END_INHERITING(SVGAElementBase
)
47 NS_IMPL_CYCLE_COLLECTION_INHERITED(SVGAElement
, SVGAElementBase
, mRelList
)
49 NS_IMPL_ADDREF_INHERITED(SVGAElement
, SVGAElementBase
)
50 NS_IMPL_RELEASE_INHERITED(SVGAElement
, SVGAElementBase
)
52 //----------------------------------------------------------------------
55 SVGAElement::SVGAElement(already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
56 : SVGAElementBase(std::move(aNodeInfo
)), Link(this) {}
58 already_AddRefed
<DOMSVGAnimatedString
> SVGAElement::Href() {
59 return mStringAttributes
[HREF
].IsExplicitlySet()
60 ? mStringAttributes
[HREF
].ToDOMAnimatedString(this)
61 : mStringAttributes
[XLINK_HREF
].ToDOMAnimatedString(this);
64 //----------------------------------------------------------------------
67 bool SVGAElement::ElementHasHref() const {
68 return mStringAttributes
[HREF
].IsExplicitlySet() ||
69 mStringAttributes
[XLINK_HREF
].IsExplicitlySet();
72 //----------------------------------------------------------------------
75 void SVGAElement::GetEventTargetParent(EventChainPreVisitor
& aVisitor
) {
76 Element::GetEventTargetParent(aVisitor
);
78 GetEventTargetParentForLinks(aVisitor
);
81 nsresult
SVGAElement::PostHandleEvent(EventChainPostVisitor
& aVisitor
) {
82 return PostHandleEventForLinks(aVisitor
);
85 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGAElement
)
87 //----------------------------------------------------------------------
89 already_AddRefed
<DOMSVGAnimatedString
> SVGAElement::Target() {
90 return mStringAttributes
[TARGET
].ToDOMAnimatedString(this);
93 void SVGAElement::GetDownload(nsAString
& aDownload
) {
94 GetAttr(nsGkAtoms::download
, aDownload
);
97 void SVGAElement::SetDownload(const nsAString
& aDownload
, ErrorResult
& rv
) {
98 SetAttr(nsGkAtoms::download
, aDownload
, rv
);
101 void SVGAElement::GetPing(nsAString
& aPing
) { GetAttr(nsGkAtoms::ping
, aPing
); }
103 void SVGAElement::SetPing(const nsAString
& aPing
, ErrorResult
& rv
) {
104 SetAttr(nsGkAtoms::ping
, aPing
, rv
);
107 void SVGAElement::GetRel(nsAString
& aRel
) { GetAttr(nsGkAtoms::rel
, aRel
); }
109 void SVGAElement::SetRel(const nsAString
& aRel
, ErrorResult
& rv
) {
110 SetAttr(nsGkAtoms::rel
, aRel
, rv
);
113 void SVGAElement::GetReferrerPolicy(nsAString
& aPolicy
) {
114 GetEnumAttr(nsGkAtoms::referrerpolicy
, EmptyCString().get(), aPolicy
);
117 void SVGAElement::SetReferrerPolicy(const nsAString
& aPolicy
,
118 mozilla::ErrorResult
& rv
) {
119 SetAttr(nsGkAtoms::referrerpolicy
, aPolicy
, rv
);
122 nsDOMTokenList
* SVGAElement::RelList() {
124 mRelList
= new nsDOMTokenList(this, nsGkAtoms::rel
, sSupportedRelValues
);
129 void SVGAElement::GetHreflang(nsAString
& aHreflang
) {
130 GetAttr(nsGkAtoms::hreflang
, aHreflang
);
133 void SVGAElement::SetHreflang(const nsAString
& aHreflang
,
134 mozilla::ErrorResult
& rv
) {
135 SetAttr(nsGkAtoms::hreflang
, aHreflang
, rv
);
138 void SVGAElement::GetType(nsAString
& aType
) { GetAttr(nsGkAtoms::type
, aType
); }
140 void SVGAElement::SetType(const nsAString
& aType
, mozilla::ErrorResult
& rv
) {
141 SetAttr(nsGkAtoms::type
, aType
, rv
);
144 void SVGAElement::GetText(nsAString
& aText
, mozilla::ErrorResult
& rv
) {
146 !nsContentUtils::GetNodeTextContent(this, true, aText
, fallible
))) {
147 rv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
151 void SVGAElement::SetText(const nsAString
& aText
, mozilla::ErrorResult
& rv
) {
152 rv
= nsContentUtils::SetNodeTextContent(this, aText
, false);
155 //----------------------------------------------------------------------
156 // nsIContent methods
158 nsresult
SVGAElement::BindToTree(BindContext
& aContext
, nsINode
& aParent
) {
159 Link::ResetLinkState(false, Link::ElementHasHref());
161 nsresult rv
= SVGAElementBase::BindToTree(aContext
, aParent
);
162 NS_ENSURE_SUCCESS(rv
, rv
);
164 if (Document
* doc
= aContext
.GetComposedDoc()) {
165 doc
->RegisterPendingLinkUpdate(this);
171 void SVGAElement::UnbindFromTree(bool aNullParent
) {
172 // Without removing the link state we risk a dangling pointer
173 // in the mStyledLinks hashtable
174 Link::ResetLinkState(false, Link::ElementHasHref());
176 SVGAElementBase::UnbindFromTree(aNullParent
);
179 already_AddRefed
<nsIURI
> SVGAElement::GetHrefURI() const {
180 nsCOMPtr
<nsIURI
> hrefURI
;
181 return IsLink(getter_AddRefs(hrefURI
)) ? hrefURI
.forget() : nullptr;
185 SVGAElement::IsAttributeMapped(const nsAtom
* name
) const {
186 static const MappedAttributeEntry
* const map
[] = {sFEFloodMap
,
188 sFontSpecificationMap
,
192 sTextContentElementsMap
,
195 return FindAttributeDependence(name
, map
) ||
196 SVGAElementBase::IsAttributeMapped(name
);
199 int32_t SVGAElement::TabIndexDefault() { return 0; }
201 bool SVGAElement::IsFocusableInternal(int32_t* aTabIndex
, bool aWithMouse
) {
202 bool isFocusable
= false;
203 if (IsSVGFocusable(&isFocusable
, aTabIndex
)) {
207 if (!OwnerDoc()->LinkHandlingEnabled()) {
211 // Links that are in an editable region should never be focusable, even if
212 // they are in a contenteditable="false" region.
213 if (nsContentUtils::IsNodeInEditableRegion(this)) {
220 if (GetTabIndexAttrValue().isNothing()) {
221 // check whether we're actually a link
222 if (!Link::HasURI()) {
223 // Not tabbable or focusable without href (bug 17605), unless
224 // forced to be via presence of nonnegative tabindex attribute
232 if (aTabIndex
&& (sTabFocusModel
& eTabFocus_linksMask
) == 0) {
239 bool SVGAElement::IsLink(nsIURI
** aURI
) const {
240 // To be a clickable XLink for styling and interaction purposes, we require:
242 // xlink:href - must be set
243 // xlink:type - must be unset or set to "" or set to "simple"
244 // xlink:show - must be unset or set to "", "new" or "replace"
245 // xlink:actuate - must be unset or set to "" or "onRequest"
247 // For any other values, we're either not a *clickable* XLink, or the end
248 // result is poorly specified. Either way, we return false.
250 static Element::AttrValuesArray sTypeVals
[] = {nsGkAtoms::_empty
,
251 nsGkAtoms::simple
, nullptr};
253 static Element::AttrValuesArray sShowVals
[] = {
254 nsGkAtoms::_empty
, nsGkAtoms::_new
, nsGkAtoms::replace
, nullptr};
256 static Element::AttrValuesArray sActuateVals
[] = {
257 nsGkAtoms::_empty
, nsGkAtoms::onRequest
, nullptr};
259 // Optimization: check for href first for early return
260 bool useBareHref
= mStringAttributes
[HREF
].IsExplicitlySet();
262 if ((useBareHref
|| mStringAttributes
[XLINK_HREF
].IsExplicitlySet()) &&
263 FindAttrValueIn(kNameSpaceID_XLink
, nsGkAtoms::type
, sTypeVals
,
264 eCaseMatters
) != Element::ATTR_VALUE_NO_MATCH
&&
265 FindAttrValueIn(kNameSpaceID_XLink
, nsGkAtoms::show
, sShowVals
,
266 eCaseMatters
) != Element::ATTR_VALUE_NO_MATCH
&&
267 FindAttrValueIn(kNameSpaceID_XLink
, nsGkAtoms::actuate
, sActuateVals
,
268 eCaseMatters
) != Element::ATTR_VALUE_NO_MATCH
) {
271 const uint8_t idx
= useBareHref
? HREF
: XLINK_HREF
;
272 mStringAttributes
[idx
].GetAnimValue(str
, this);
273 nsContentUtils::NewURIWithDocumentCharset(aURI
, str
, OwnerDoc(),
275 // must promise out param is non-null if we return true
283 void SVGAElement::GetLinkTarget(nsAString
& aTarget
) {
284 mStringAttributes
[TARGET
].GetAnimValue(aTarget
, this);
285 if (aTarget
.IsEmpty()) {
286 static Element::AttrValuesArray sShowVals
[] = {nsGkAtoms::_new
,
287 nsGkAtoms::replace
, nullptr};
289 switch (FindAttrValueIn(kNameSpaceID_XLink
, nsGkAtoms::show
, sShowVals
,
292 aTarget
.AssignLiteral("_blank");
297 Document
* ownerDoc
= OwnerDoc();
299 ownerDoc
->GetBaseTarget(aTarget
);
304 EventStates
SVGAElement::IntrinsicState() const {
305 return Link::LinkState() | SVGAElementBase::IntrinsicState();
308 nsresult
SVGAElement::AfterSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
309 const nsAttrValue
* aValue
,
310 const nsAttrValue
* aOldValue
,
311 nsIPrincipal
* aMaybeScriptedPrincipal
,
313 if (aName
== nsGkAtoms::href
&& (aNameSpaceID
== kNameSpaceID_XLink
||
314 aNameSpaceID
== kNameSpaceID_None
)) {
315 // We can't assume that null aValue means we no longer have an href, because
316 // we could be unsetting xlink:href but still have a null-namespace href, or
317 // vice versa. But we can fast-path the case when we _do_ have a new value.
318 Link::ResetLinkState(aNotify
, aValue
|| Link::ElementHasHref());
321 return SVGAElementBase::AfterSetAttr(aNameSpaceID
, aName
, aValue
, aOldValue
,
322 aMaybeScriptedPrincipal
, aNotify
);
325 //----------------------------------------------------------------------
326 // SVGElement methods
328 SVGElement::StringAttributesInfo
SVGAElement::GetStringInfo() {
329 return StringAttributesInfo(mStringAttributes
, sStringInfo
,
330 ArrayLength(sStringInfo
));
334 } // namespace mozilla