SVGAElement.cpp (9177B)
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/EventDispatcher.h" 10 #include "mozilla/FocusModel.h" 11 #include "mozilla/dom/DocumentInlines.h" 12 #include "mozilla/dom/SVGAElementBinding.h" 13 #include "nsCOMPtr.h" 14 #include "nsContentUtils.h" 15 #include "nsGkAtoms.h" 16 #include "nsIContentInlines.h" 17 #include "nsIURI.h" 18 19 NS_IMPL_NS_NEW_SVG_ELEMENT(A) 20 21 namespace mozilla::dom { 22 23 JSObject* SVGAElement::WrapNode(JSContext* aCx, 24 JS::Handle<JSObject*> aGivenProto) { 25 return SVGAElement_Binding::Wrap(aCx, this, aGivenProto); 26 } 27 28 SVGElement::StringInfo SVGAElement::sStringInfo[3] = { 29 {nsGkAtoms::href, kNameSpaceID_None, true}, 30 {nsGkAtoms::href, kNameSpaceID_XLink, true}, 31 {nsGkAtoms::target, kNameSpaceID_None, true}}; 32 33 //---------------------------------------------------------------------- 34 // nsISupports methods 35 36 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGAElement) 37 NS_INTERFACE_MAP_ENTRY(Link) 38 NS_INTERFACE_MAP_END_INHERITING(SVGAElementBase) 39 40 NS_IMPL_CYCLE_COLLECTION_INHERITED(SVGAElement, SVGAElementBase, mRelList) 41 42 NS_IMPL_ADDREF_INHERITED(SVGAElement, SVGAElementBase) 43 NS_IMPL_RELEASE_INHERITED(SVGAElement, SVGAElementBase) 44 45 //---------------------------------------------------------------------- 46 // Implementation 47 48 SVGAElement::SVGAElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) 49 : SVGAElementBase(std::move(aNodeInfo)), Link(this) {} 50 51 already_AddRefed<DOMSVGAnimatedString> SVGAElement::Href() { 52 return mStringAttributes[HREF].IsExplicitlySet() || 53 !mStringAttributes[XLINK_HREF].IsExplicitlySet() 54 ? mStringAttributes[HREF].ToDOMAnimatedString(this) 55 : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this); 56 } 57 58 //---------------------------------------------------------------------- 59 // nsINode methods 60 61 void SVGAElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { 62 Element::GetEventTargetParent(aVisitor); 63 64 GetEventTargetParentForLinks(aVisitor); 65 } 66 67 nsresult SVGAElement::PostHandleEvent(EventChainPostVisitor& aVisitor) { 68 return PostHandleEventForLinks(aVisitor); 69 } 70 71 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGAElement) 72 73 //---------------------------------------------------------------------- 74 75 already_AddRefed<DOMSVGAnimatedString> SVGAElement::Target() { 76 return mStringAttributes[TARGET].ToDOMAnimatedString(this); 77 } 78 79 void SVGAElement::GetDownload(nsAString& aDownload) { 80 GetAttr(nsGkAtoms::download, aDownload); 81 } 82 83 void SVGAElement::SetDownload(const nsAString& aDownload, ErrorResult& rv) { 84 SetAttr(nsGkAtoms::download, aDownload, rv); 85 } 86 87 void SVGAElement::GetPing(nsAString& aPing) { GetAttr(nsGkAtoms::ping, aPing); } 88 89 void SVGAElement::SetPing(const nsAString& aPing, ErrorResult& rv) { 90 SetAttr(nsGkAtoms::ping, aPing, rv); 91 } 92 93 void SVGAElement::GetRel(nsAString& aRel) { GetAttr(nsGkAtoms::rel, aRel); } 94 95 void SVGAElement::SetRel(const nsAString& aRel, ErrorResult& rv) { 96 SetAttr(nsGkAtoms::rel, aRel, rv); 97 } 98 99 void SVGAElement::GetReferrerPolicy(nsAString& aPolicy) { 100 GetEnumAttr(nsGkAtoms::referrerpolicy, "", aPolicy); 101 } 102 103 void SVGAElement::SetReferrerPolicy(const nsAString& aPolicy, 104 mozilla::ErrorResult& rv) { 105 SetAttr(nsGkAtoms::referrerpolicy, aPolicy, rv); 106 } 107 108 nsDOMTokenList* SVGAElement::RelList() { 109 if (!mRelList) { 110 mRelList = 111 new nsDOMTokenList(this, nsGkAtoms::rel, sAnchorAndFormRelValues); 112 } 113 return mRelList; 114 } 115 116 void SVGAElement::GetHreflang(nsAString& aHreflang) { 117 GetAttr(nsGkAtoms::hreflang, aHreflang); 118 } 119 120 void SVGAElement::SetHreflang(const nsAString& aHreflang, 121 mozilla::ErrorResult& rv) { 122 SetAttr(nsGkAtoms::hreflang, aHreflang, rv); 123 } 124 125 void SVGAElement::GetType(nsAString& aType) { GetAttr(nsGkAtoms::type, aType); } 126 127 void SVGAElement::SetType(const nsAString& aType, mozilla::ErrorResult& rv) { 128 SetAttr(nsGkAtoms::type, aType, rv); 129 } 130 131 void SVGAElement::GetText(nsAString& aText, mozilla::ErrorResult& rv) const { 132 if (NS_WARN_IF( 133 !nsContentUtils::GetNodeTextContent(this, true, aText, fallible))) { 134 rv.Throw(NS_ERROR_OUT_OF_MEMORY); 135 } 136 } 137 138 void SVGAElement::SetText(const nsAString& aText, mozilla::ErrorResult& rv) { 139 rv = nsContentUtils::SetNodeTextContent(this, aText, false); 140 } 141 142 //---------------------------------------------------------------------- 143 // nsIContent methods 144 145 nsresult SVGAElement::BindToTree(BindContext& aContext, nsINode& aParent) { 146 nsresult rv = SVGAElementBase::BindToTree(aContext, aParent); 147 NS_ENSURE_SUCCESS(rv, rv); 148 Link::BindToTree(aContext); 149 return NS_OK; 150 } 151 152 void SVGAElement::UnbindFromTree(UnbindContext& aContext) { 153 SVGAElementBase::UnbindFromTree(aContext); 154 // Without removing the link state we risk a dangling pointer 155 // in the mStyledLinks hashtable 156 Link::UnbindFromTree(); 157 } 158 159 int32_t SVGAElement::TabIndexDefault() { return 0; } 160 161 Focusable SVGAElement::IsFocusableWithoutStyle(IsFocusableFlags) { 162 Focusable result; 163 if (IsSVGFocusable(&result.mFocusable, &result.mTabIndex)) { 164 return result; 165 } 166 167 if (!OwnerDoc()->LinkHandlingEnabled()) { 168 return {}; 169 } 170 171 // Links that are in an editable region should never be focusable, even if 172 // they are in a contenteditable="false" region. 173 if (nsContentUtils::IsNodeInEditableRegion(this)) { 174 return {}; 175 } 176 177 if (GetTabIndexAttrValue().isNothing()) { 178 // check whether we're actually a link 179 if (!IsLink()) { 180 // Not tabbable or focusable without href (bug 17605), unless 181 // forced to be via presence of nonnegative tabindex attribute 182 return {}; 183 } 184 } 185 if (!FocusModel::IsTabFocusable(TabFocusableType::Links)) { 186 result.mTabIndex = -1; 187 } 188 return result; 189 } 190 191 bool SVGAElement::HasHref() const { 192 // Currently our SMIL implementation does not modify the DOM attributes. Once 193 // we implement the SVG 2 SMIL behaviour this can be removed. 194 return mStringAttributes[HREF].IsExplicitlySet() || 195 mStringAttributes[XLINK_HREF].IsExplicitlySet(); 196 } 197 198 already_AddRefed<nsIURI> SVGAElement::GetHrefURI() const { 199 // Optimization: check for href first for early return 200 bool useBareHref = mStringAttributes[HREF].IsExplicitlySet(); 201 if (useBareHref || mStringAttributes[XLINK_HREF].IsExplicitlySet()) { 202 // Get absolute URI 203 nsAutoString str; 204 const uint8_t idx = useBareHref ? HREF : XLINK_HREF; 205 mStringAttributes[idx].GetAnimValue(str, this); 206 nsCOMPtr<nsIURI> uri; 207 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), str, 208 OwnerDoc(), GetBaseURI()); 209 return uri.forget(); 210 } 211 return nullptr; 212 } 213 214 void SVGAElement::GetLinkTargetImpl(nsAString& aTarget) { 215 mStringAttributes[TARGET].GetAnimValue(aTarget, this); 216 if (aTarget.IsEmpty()) { 217 static Element::AttrValuesArray sShowVals[] = {nsGkAtoms::_new, 218 nsGkAtoms::replace, nullptr}; 219 220 switch (FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show, sShowVals, 221 eCaseMatters)) { 222 case 0: 223 aTarget.AssignLiteral("_blank"); 224 return; 225 case 1: 226 return; 227 } 228 Document* ownerDoc = OwnerDoc(); 229 if (ownerDoc) { 230 ownerDoc->GetBaseTarget(aTarget); 231 } 232 } 233 } 234 235 void SVGAElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName, 236 const nsAttrValue* aValue, 237 const nsAttrValue* aOldValue, 238 nsIPrincipal* aMaybeScriptedPrincipal, 239 bool aNotify) { 240 if (aName == nsGkAtoms::href && (aNameSpaceID == kNameSpaceID_XLink || 241 aNameSpaceID == kNameSpaceID_None)) { 242 // We can't assume that null aValue means we no longer have an href, because 243 // we could be unsetting xlink:href but still have a null-namespace href, or 244 // vice versa. But we can fast-path the case when we _do_ have a new value. 245 Link::ResetLinkState(aNotify, aValue || Link::ElementHasHref()); 246 } 247 248 return SVGAElementBase::AfterSetAttr(aNameSpaceID, aName, aValue, aOldValue, 249 aMaybeScriptedPrincipal, aNotify); 250 } 251 252 //---------------------------------------------------------------------- 253 // SVGElement methods 254 255 SVGElement::StringAttributesInfo SVGAElement::GetStringInfo() { 256 return StringAttributesInfo(mStringAttributes, sStringInfo, 257 std::size(sStringInfo)); 258 } 259 260 void SVGAElement::DidAnimateAttribute(int32_t aNameSpaceID, 261 nsAtom* aAttribute) { 262 if ((aNameSpaceID == kNameSpaceID_None || 263 aNameSpaceID == kNameSpaceID_XLink) && 264 aAttribute == nsGkAtoms::href) { 265 Link::ResetLinkState(true, Link::ElementHasHref()); 266 } 267 SVGAElementBase::DidAnimateAttribute(aNameSpaceID, aAttribute); 268 } 269 270 } // namespace mozilla::dom