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/HTMLAnchorElement.h"
9 #include "mozilla/dom/BindContext.h"
10 #include "mozilla/dom/HTMLAnchorElementBinding.h"
11 #include "mozilla/EventDispatcher.h"
12 #include "mozilla/EventStates.h"
13 #include "mozilla/MemoryReporting.h"
15 #include "nsContentUtils.h"
16 #include "nsGkAtoms.h"
17 #include "nsHTMLDNSPrefetch.h"
18 #include "nsAttrValueOrString.h"
19 #include "mozilla/dom/Document.h"
20 #include "nsPresContext.h"
22 #include "nsWindowSizes.h"
24 NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor
)
29 #define ANCHOR_ELEMENT_FLAG_BIT(n_) \
30 NODE_FLAG_BIT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + (n_))
32 // Anchor element specific bits
34 // Indicates that a DNS Prefetch has been requested from this Anchor elem
35 HTML_ANCHOR_DNS_PREFETCH_REQUESTED
= ANCHOR_ELEMENT_FLAG_BIT(0),
37 // Indicates that a DNS Prefetch was added to the deferral queue
38 HTML_ANCHOR_DNS_PREFETCH_DEFERRED
= ANCHOR_ELEMENT_FLAG_BIT(1)
41 ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET
+ 2);
43 #undef ANCHOR_ELEMENT_FLAG_BIT
46 const DOMTokenListSupportedToken
HTMLAnchorElement::sSupportedRelValues
[] = {
47 "noreferrer", "noopener", nullptr};
49 HTMLAnchorElement::~HTMLAnchorElement() = default;
51 bool HTMLAnchorElement::IsInteractiveHTMLContent() const {
52 return HasAttr(kNameSpaceID_None
, nsGkAtoms::href
) ||
53 nsGenericHTMLElement::IsInteractiveHTMLContent();
56 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLAnchorElement
,
57 nsGenericHTMLElement
, Link
)
59 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLAnchorElement
, nsGenericHTMLElement
,
62 NS_IMPL_ELEMENT_CLONE(HTMLAnchorElement
)
64 JSObject
* HTMLAnchorElement::WrapNode(JSContext
* aCx
,
65 JS::Handle
<JSObject
*> aGivenProto
) {
66 return HTMLAnchorElement_Binding::Wrap(aCx
, this, aGivenProto
);
69 int32_t HTMLAnchorElement::TabIndexDefault() { return 0; }
71 bool HTMLAnchorElement::Draggable() const {
72 // links can be dragged as long as there is an href and the
73 // draggable attribute isn't false
74 if (!HasAttr(kNameSpaceID_None
, nsGkAtoms::href
)) {
75 // no href, so just use the same behavior as other elements
76 return nsGenericHTMLElement::Draggable();
79 return !AttrValueIs(kNameSpaceID_None
, nsGkAtoms::draggable
,
80 nsGkAtoms::_false
, eIgnoreCase
);
83 void HTMLAnchorElement::OnDNSPrefetchRequested() {
84 UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED
);
85 SetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED
);
88 void HTMLAnchorElement::OnDNSPrefetchDeferred() {
89 UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED
);
90 SetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED
);
93 bool HTMLAnchorElement::HasDeferredDNSPrefetchRequest() {
94 return HasFlag(HTML_ANCHOR_DNS_PREFETCH_DEFERRED
);
97 nsresult
HTMLAnchorElement::BindToTree(BindContext
& aContext
,
99 Link::ResetLinkState(false, Link::ElementHasHref());
101 nsresult rv
= nsGenericHTMLElement::BindToTree(aContext
, aParent
);
102 NS_ENSURE_SUCCESS(rv
, rv
);
105 if (IsInComposedDoc()) {
106 aContext
.OwnerDoc().RegisterPendingLinkUpdate(this);
113 void HTMLAnchorElement::UnbindFromTree(bool aNullParent
) {
114 // Cancel any DNS prefetches
115 // Note: Must come before ResetLinkState. If called after, it will recreate
116 // mCachedURI based on data that is invalid - due to a call to GetHostname.
117 CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED
,
118 HTML_ANCHOR_DNS_PREFETCH_REQUESTED
);
120 // Without removing the link state we risk a dangling pointer
121 // in the mStyledLinks hashtable
122 Link::ResetLinkState(false, Link::ElementHasHref());
124 nsGenericHTMLElement::UnbindFromTree(aNullParent
);
127 bool HTMLAnchorElement::IsHTMLFocusable(bool aWithMouse
, bool* aIsFocusable
,
128 int32_t* aTabIndex
) {
129 if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse
, aIsFocusable
,
134 // cannot focus links if there is no link handler
135 if (!OwnerDoc()->LinkHandlingEnabled()) {
136 *aIsFocusable
= false;
140 // Links that are in an editable region should never be focusable, even if
141 // they are in a contenteditable="false" region.
142 if (nsContentUtils::IsNodeInEditableRegion(this)) {
147 *aIsFocusable
= false;
152 if (GetTabIndexAttrValue().isNothing()) {
153 // check whether we're actually a link
154 if (!Link::HasURI()) {
155 // Not tabbable or focusable without href (bug 17605), unless
156 // forced to be via presence of nonnegative tabindex attribute
161 *aIsFocusable
= false;
167 if (aTabIndex
&& (sTabFocusModel
& eTabFocus_linksMask
) == 0) {
171 *aIsFocusable
= true;
176 void HTMLAnchorElement::GetEventTargetParent(EventChainPreVisitor
& aVisitor
) {
177 GetEventTargetParentForAnchors(aVisitor
);
180 nsresult
HTMLAnchorElement::PostHandleEvent(EventChainPostVisitor
& aVisitor
) {
181 return PostHandleEventForAnchors(aVisitor
);
184 bool HTMLAnchorElement::IsLink(nsIURI
** aURI
) const { return IsHTMLLink(aURI
); }
186 void HTMLAnchorElement::GetLinkTarget(nsAString
& aTarget
) {
187 GetAttr(kNameSpaceID_None
, nsGkAtoms::target
, aTarget
);
188 if (aTarget
.IsEmpty()) {
189 GetBaseTarget(aTarget
);
193 void HTMLAnchorElement::GetTarget(nsAString
& aValue
) {
194 if (!GetAttr(kNameSpaceID_None
, nsGkAtoms::target
, aValue
)) {
195 GetBaseTarget(aValue
);
199 nsDOMTokenList
* HTMLAnchorElement::RelList() {
201 mRelList
= new nsDOMTokenList(this, nsGkAtoms::rel
, sSupportedRelValues
);
206 void HTMLAnchorElement::GetText(nsAString
& aText
, mozilla::ErrorResult
& aRv
) {
208 !nsContentUtils::GetNodeTextContent(this, true, aText
, fallible
))) {
209 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
213 void HTMLAnchorElement::SetText(const nsAString
& aText
, ErrorResult
& aRv
) {
214 aRv
= nsContentUtils::SetNodeTextContent(this, aText
, false);
217 void HTMLAnchorElement::ToString(nsAString
& aSource
) {
218 return GetHref(aSource
);
221 already_AddRefed
<nsIURI
> HTMLAnchorElement::GetHrefURI() const {
222 nsCOMPtr
<nsIURI
> uri
= Link::GetCachedURI();
227 return GetHrefURIForAnchors();
230 nsresult
HTMLAnchorElement::BeforeSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
231 const nsAttrValueOrString
* aValue
,
233 if (aNamespaceID
== kNameSpaceID_None
) {
234 if (aName
== nsGkAtoms::href
) {
235 CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED
,
236 HTML_ANCHOR_DNS_PREFETCH_REQUESTED
);
240 return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID
, aName
, aValue
,
244 nsresult
HTMLAnchorElement::AfterSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
245 const nsAttrValue
* aValue
,
246 const nsAttrValue
* aOldValue
,
247 nsIPrincipal
* aSubjectPrincipal
,
249 if (aNamespaceID
== kNameSpaceID_None
) {
250 if (aName
== nsGkAtoms::href
) {
251 Link::ResetLinkState(aNotify
, !!aValue
);
252 if (aValue
&& IsInComposedDoc()) {
258 return nsGenericHTMLElement::AfterSetAttr(
259 aNamespaceID
, aName
, aValue
, aOldValue
, aSubjectPrincipal
, aNotify
);
262 EventStates
HTMLAnchorElement::IntrinsicState() const {
263 return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
266 void HTMLAnchorElement::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
267 size_t* aNodeSize
) const {
268 nsGenericHTMLElement::AddSizeOfExcludingThis(aSizes
, aNodeSize
);
269 *aNodeSize
+= Link::SizeOfExcludingThis(aSizes
.mState
);
273 } // namespace mozilla