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/HTMLAnchorElement.h"
8 
9 #include "mozilla/dom/BindContext.h"
10 #include "mozilla/dom/HTMLAnchorElementBinding.h"
11 #include "mozilla/dom/HTMLDNSPrefetch.h"
12 #include "mozilla/EventDispatcher.h"
13 #include "mozilla/EventStates.h"
14 #include "mozilla/MemoryReporting.h"
15 #include "nsCOMPtr.h"
16 #include "nsContentUtils.h"
17 #include "nsGkAtoms.h"
18 #include "nsAttrValueOrString.h"
19 #include "mozilla/dom/Document.h"
20 #include "nsPresContext.h"
21 #include "nsIURI.h"
22 #include "nsWindowSizes.h"
23 
24 NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor)
25 
26 namespace mozilla::dom {
27 
28 // static
29 const DOMTokenListSupportedToken HTMLAnchorElement::sSupportedRelValues[] = {
30     "noreferrer", "noopener", nullptr};
31 
~HTMLAnchorElement()32 HTMLAnchorElement::~HTMLAnchorElement() {
33   SupportsDNSPrefetch::Destroyed(*this);
34 }
35 
IsInteractiveHTMLContent() const36 bool HTMLAnchorElement::IsInteractiveHTMLContent() const {
37   return HasAttr(kNameSpaceID_None, nsGkAtoms::href) ||
38          nsGenericHTMLElement::IsInteractiveHTMLContent();
39 }
40 
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLAnchorElement,nsGenericHTMLElement,Link)41 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLAnchorElement,
42                                              nsGenericHTMLElement, Link)
43 
44 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLAnchorElement, nsGenericHTMLElement,
45                                    mRelList)
46 
47 NS_IMPL_ELEMENT_CLONE(HTMLAnchorElement)
48 
49 JSObject* HTMLAnchorElement::WrapNode(JSContext* aCx,
50                                       JS::Handle<JSObject*> aGivenProto) {
51   return HTMLAnchorElement_Binding::Wrap(aCx, this, aGivenProto);
52 }
53 
TabIndexDefault()54 int32_t HTMLAnchorElement::TabIndexDefault() { return 0; }
55 
Draggable() const56 bool HTMLAnchorElement::Draggable() const {
57   // links can be dragged as long as there is an href and the
58   // draggable attribute isn't false
59   if (!HasAttr(kNameSpaceID_None, nsGkAtoms::href)) {
60     // no href, so just use the same behavior as other elements
61     return nsGenericHTMLElement::Draggable();
62   }
63 
64   return !AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
65                       nsGkAtoms::_false, eIgnoreCase);
66 }
67 
BindToTree(BindContext & aContext,nsINode & aParent)68 nsresult HTMLAnchorElement::BindToTree(BindContext& aContext,
69                                        nsINode& aParent) {
70   Link::ResetLinkState(false, Link::ElementHasHref());
71 
72   nsresult rv = nsGenericHTMLElement::BindToTree(aContext, aParent);
73   NS_ENSURE_SUCCESS(rv, rv);
74 
75   // Prefetch links
76   if (IsInComposedDoc()) {
77     aContext.OwnerDoc().RegisterPendingLinkUpdate(this);
78     TryDNSPrefetch(*this);
79   }
80 
81   return rv;
82 }
83 
UnbindFromTree(bool aNullParent)84 void HTMLAnchorElement::UnbindFromTree(bool aNullParent) {
85   // Cancel any DNS prefetches
86   // Note: Must come before ResetLinkState.  If called after, it will recreate
87   // mCachedURI based on data that is invalid - due to a call to Link::GetURI()
88   // via GetURIForDNSPrefetch().
89   CancelDNSPrefetch(*this);
90 
91   // Without removing the link state we risk a dangling pointer
92   // in the mStyledLinks hashtable
93   Link::ResetLinkState(false, Link::ElementHasHref());
94 
95   nsGenericHTMLElement::UnbindFromTree(aNullParent);
96 }
97 
IsHTMLFocusable(bool aWithMouse,bool * aIsFocusable,int32_t * aTabIndex)98 bool HTMLAnchorElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
99                                         int32_t* aTabIndex) {
100   if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable,
101                                             aTabIndex)) {
102     return true;
103   }
104 
105   // cannot focus links if there is no link handler
106   if (!OwnerDoc()->LinkHandlingEnabled()) {
107     *aIsFocusable = false;
108     return false;
109   }
110 
111   // Links that are in an editable region should never be focusable, even if
112   // they are in a contenteditable="false" region.
113   if (nsContentUtils::IsNodeInEditableRegion(this)) {
114     if (aTabIndex) {
115       *aTabIndex = -1;
116     }
117 
118     *aIsFocusable = false;
119 
120     return true;
121   }
122 
123   if (GetTabIndexAttrValue().isNothing()) {
124     // check whether we're actually a link
125     if (!Link::HasURI()) {
126       // Not tabbable or focusable without href (bug 17605), unless
127       // forced to be via presence of nonnegative tabindex attribute
128       if (aTabIndex) {
129         *aTabIndex = -1;
130       }
131 
132       *aIsFocusable = false;
133 
134       return false;
135     }
136   }
137 
138   if (aTabIndex && (sTabFocusModel & eTabFocus_linksMask) == 0) {
139     *aTabIndex = -1;
140   }
141 
142   *aIsFocusable = true;
143 
144   return false;
145 }
146 
GetEventTargetParent(EventChainPreVisitor & aVisitor)147 void HTMLAnchorElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
148   GetEventTargetParentForAnchors(aVisitor);
149 }
150 
PostHandleEvent(EventChainPostVisitor & aVisitor)151 nsresult HTMLAnchorElement::PostHandleEvent(EventChainPostVisitor& aVisitor) {
152   return PostHandleEventForAnchors(aVisitor);
153 }
154 
IsLink(nsIURI ** aURI) const155 bool HTMLAnchorElement::IsLink(nsIURI** aURI) const { return IsHTMLLink(aURI); }
156 
GetLinkTarget(nsAString & aTarget)157 void HTMLAnchorElement::GetLinkTarget(nsAString& aTarget) {
158   GetAttr(kNameSpaceID_None, nsGkAtoms::target, aTarget);
159   if (aTarget.IsEmpty()) {
160     GetBaseTarget(aTarget);
161   }
162 }
163 
GetTarget(nsAString & aValue)164 void HTMLAnchorElement::GetTarget(nsAString& aValue) {
165   if (!GetAttr(kNameSpaceID_None, nsGkAtoms::target, aValue)) {
166     GetBaseTarget(aValue);
167   }
168 }
169 
RelList()170 nsDOMTokenList* HTMLAnchorElement::RelList() {
171   if (!mRelList) {
172     mRelList = new nsDOMTokenList(this, nsGkAtoms::rel, sSupportedRelValues);
173   }
174   return mRelList;
175 }
176 
GetText(nsAString & aText,mozilla::ErrorResult & aRv)177 void HTMLAnchorElement::GetText(nsAString& aText, mozilla::ErrorResult& aRv) {
178   if (NS_WARN_IF(
179           !nsContentUtils::GetNodeTextContent(this, true, aText, fallible))) {
180     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
181   }
182 }
183 
SetText(const nsAString & aText,ErrorResult & aRv)184 void HTMLAnchorElement::SetText(const nsAString& aText, ErrorResult& aRv) {
185   aRv = nsContentUtils::SetNodeTextContent(this, aText, false);
186 }
187 
ToString(nsAString & aSource)188 void HTMLAnchorElement::ToString(nsAString& aSource) {
189   return GetHref(aSource);
190 }
191 
GetHrefURI() const192 already_AddRefed<nsIURI> HTMLAnchorElement::GetHrefURI() const {
193   nsCOMPtr<nsIURI> uri = Link::GetCachedURI();
194   if (uri) {
195     return uri.forget();
196   }
197 
198   return GetHrefURIForAnchors();
199 }
200 
BeforeSetAttr(int32_t aNamespaceID,nsAtom * aName,const nsAttrValueOrString * aValue,bool aNotify)201 nsresult HTMLAnchorElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
202                                           const nsAttrValueOrString* aValue,
203                                           bool aNotify) {
204   if (aNamespaceID == kNameSpaceID_None) {
205     if (aName == nsGkAtoms::href) {
206       CancelDNSPrefetch(*this);
207     }
208   }
209 
210   return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, aValue,
211                                              aNotify);
212 }
213 
AfterSetAttr(int32_t aNamespaceID,nsAtom * aName,const nsAttrValue * aValue,const nsAttrValue * aOldValue,nsIPrincipal * aSubjectPrincipal,bool aNotify)214 nsresult HTMLAnchorElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
215                                          const nsAttrValue* aValue,
216                                          const nsAttrValue* aOldValue,
217                                          nsIPrincipal* aSubjectPrincipal,
218                                          bool aNotify) {
219   if (aNamespaceID == kNameSpaceID_None) {
220     if (aName == nsGkAtoms::href) {
221       Link::ResetLinkState(aNotify, !!aValue);
222       if (aValue && IsInComposedDoc()) {
223         TryDNSPrefetch(*this);
224       }
225     }
226   }
227 
228   return nsGenericHTMLElement::AfterSetAttr(
229       aNamespaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify);
230 }
231 
IntrinsicState() const232 EventStates HTMLAnchorElement::IntrinsicState() const {
233   return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
234 }
235 
AddSizeOfExcludingThis(nsWindowSizes & aSizes,size_t * aNodeSize) const236 void HTMLAnchorElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
237                                                size_t* aNodeSize) const {
238   nsGenericHTMLElement::AddSizeOfExcludingThis(aSizes, aNodeSize);
239   *aNodeSize += Link::SizeOfExcludingThis(aSizes.mState);
240 }
241 
242 }  // namespace mozilla::dom
243