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() {}
51 bool HTMLAnchorElement::IsInteractiveHTMLContent(bool aIgnoreTabindex
) const {
52 return HasAttr(kNameSpaceID_None
, nsGkAtoms::href
) ||
53 nsGenericHTMLElement::IsInteractiveHTMLContent(aIgnoreTabindex
);
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 static bool IsNodeInEditableRegion(nsINode
* aNode
) {
129 if (aNode
->IsEditable()) {
132 aNode
= aNode
->GetParent();
137 bool HTMLAnchorElement::IsHTMLFocusable(bool aWithMouse
, bool* aIsFocusable
,
138 int32_t* aTabIndex
) {
139 if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse
, aIsFocusable
,
144 // cannot focus links if there is no link handler
145 if (!OwnerDoc()->LinkHandlingEnabled()) {
146 *aIsFocusable
= false;
150 // Links that are in an editable region should never be focusable, even if
151 // they are in a contenteditable="false" region.
152 if (IsNodeInEditableRegion(this)) {
157 *aIsFocusable
= false;
162 if (!HasAttr(kNameSpaceID_None
, nsGkAtoms::tabindex
)) {
163 // check whether we're actually a link
164 if (!Link::HasURI()) {
165 // Not tabbable or focusable without href (bug 17605), unless
166 // forced to be via presence of nonnegative tabindex attribute
171 *aIsFocusable
= false;
177 if (aTabIndex
&& (sTabFocusModel
& eTabFocus_linksMask
) == 0) {
181 *aIsFocusable
= true;
186 void HTMLAnchorElement::GetEventTargetParent(EventChainPreVisitor
& aVisitor
) {
187 GetEventTargetParentForAnchors(aVisitor
);
190 nsresult
HTMLAnchorElement::PostHandleEvent(EventChainPostVisitor
& aVisitor
) {
191 return PostHandleEventForAnchors(aVisitor
);
194 bool HTMLAnchorElement::IsLink(nsIURI
** aURI
) const { return IsHTMLLink(aURI
); }
196 void HTMLAnchorElement::GetLinkTarget(nsAString
& aTarget
) {
197 GetAttr(kNameSpaceID_None
, nsGkAtoms::target
, aTarget
);
198 if (aTarget
.IsEmpty()) {
199 GetBaseTarget(aTarget
);
203 void HTMLAnchorElement::GetTarget(nsAString
& aValue
) {
204 if (!GetAttr(kNameSpaceID_None
, nsGkAtoms::target
, aValue
)) {
205 GetBaseTarget(aValue
);
209 nsDOMTokenList
* HTMLAnchorElement::RelList() {
211 mRelList
= new nsDOMTokenList(this, nsGkAtoms::rel
, sSupportedRelValues
);
216 void HTMLAnchorElement::GetText(nsAString
& aText
, mozilla::ErrorResult
& aRv
) {
218 !nsContentUtils::GetNodeTextContent(this, true, aText
, fallible
))) {
219 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
223 void HTMLAnchorElement::SetText(const nsAString
& aText
, ErrorResult
& aRv
) {
224 aRv
= nsContentUtils::SetNodeTextContent(this, aText
, false);
227 void HTMLAnchorElement::ToString(nsAString
& aSource
) {
228 return GetHref(aSource
);
231 already_AddRefed
<nsIURI
> HTMLAnchorElement::GetHrefURI() const {
232 nsCOMPtr
<nsIURI
> uri
= Link::GetCachedURI();
237 return GetHrefURIForAnchors();
240 nsresult
HTMLAnchorElement::BeforeSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
241 const nsAttrValueOrString
* aValue
,
243 if (aNamespaceID
== kNameSpaceID_None
) {
244 if (aName
== nsGkAtoms::href
) {
245 CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED
,
246 HTML_ANCHOR_DNS_PREFETCH_REQUESTED
);
250 return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID
, aName
, aValue
,
254 nsresult
HTMLAnchorElement::AfterSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
255 const nsAttrValue
* aValue
,
256 const nsAttrValue
* aOldValue
,
257 nsIPrincipal
* aSubjectPrincipal
,
259 if (aNamespaceID
== kNameSpaceID_None
) {
260 if (aName
== nsGkAtoms::href
) {
261 Link::ResetLinkState(aNotify
, !!aValue
);
262 if (aValue
&& IsInComposedDoc()) {
268 return nsGenericHTMLElement::AfterSetAttr(
269 aNamespaceID
, aName
, aValue
, aOldValue
, aSubjectPrincipal
, aNotify
);
272 EventStates
HTMLAnchorElement::IntrinsicState() const {
273 return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
276 void HTMLAnchorElement::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
277 size_t* aNodeSize
) const {
278 nsGenericHTMLElement::AddSizeOfExcludingThis(aSizes
, aNodeSize
);
279 *aNodeSize
+= Link::SizeOfExcludingThis(aSizes
.mState
);
283 } // namespace mozilla