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/HTMLAnchorElementBinding.h"
10 #include "mozilla/EventDispatcher.h"
11 #include "mozilla/EventStates.h"
12 #include "mozilla/MemoryReporting.h"
14 #include "nsContentUtils.h"
15 #include "nsGkAtoms.h"
16 #include "nsHTMLDNSPrefetch.h"
17 #include "nsAttrValueOrString.h"
18 #include "nsIDocument.h"
19 #include "nsIPresShell.h"
20 #include "nsPresContext.h"
22 #include "nsWindowSizes.h"
24 NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor
)
29 #define ANCHOR_ELEMENT_FLAG_BIT(n_) NODE_FLAG_BIT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + (n_))
31 // Anchor element specific bits
33 // Indicates that a DNS Prefetch has been requested from this Anchor elem
34 HTML_ANCHOR_DNS_PREFETCH_REQUESTED
= ANCHOR_ELEMENT_FLAG_BIT(0),
36 // Indicates that a DNS Prefetch was added to the deferral queue
37 HTML_ANCHOR_DNS_PREFETCH_DEFERRED
= ANCHOR_ELEMENT_FLAG_BIT(1)
40 ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET
+ 2);
42 #undef ANCHOR_ELEMENT_FLAG_BIT
45 const DOMTokenListSupportedToken
HTMLAnchorElement::sSupportedRelValues
[] = {
51 HTMLAnchorElement::~HTMLAnchorElement()
56 HTMLAnchorElement::IsInteractiveHTMLContent(bool aIgnoreTabindex
) const
58 return HasAttr(kNameSpaceID_None
, nsGkAtoms::href
) ||
59 nsGenericHTMLElement::IsInteractiveHTMLContent(aIgnoreTabindex
);
62 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLAnchorElement
,
66 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLAnchorElement
,
70 NS_IMPL_ELEMENT_CLONE(HTMLAnchorElement
)
73 HTMLAnchorElement::WrapNode(JSContext
*aCx
, JS::Handle
<JSObject
*> aGivenProto
)
75 return HTMLAnchorElement_Binding::Wrap(aCx
, this, aGivenProto
);
79 HTMLAnchorElement::TabIndexDefault()
85 HTMLAnchorElement::Draggable() const
87 // links can be dragged as long as there is an href and the
88 // draggable attribute isn't false
89 if (!HasAttr(kNameSpaceID_None
, nsGkAtoms::href
)) {
90 // no href, so just use the same behavior as other elements
91 return nsGenericHTMLElement::Draggable();
94 return !AttrValueIs(kNameSpaceID_None
, nsGkAtoms::draggable
,
95 nsGkAtoms::_false
, eIgnoreCase
);
99 HTMLAnchorElement::OnDNSPrefetchRequested()
101 UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED
);
102 SetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED
);
106 HTMLAnchorElement::OnDNSPrefetchDeferred()
108 UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED
);
109 SetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED
);
113 HTMLAnchorElement::HasDeferredDNSPrefetchRequest()
115 return HasFlag(HTML_ANCHOR_DNS_PREFETCH_DEFERRED
);
119 HTMLAnchorElement::BindToTree(nsIDocument
* aDocument
, nsIContent
* aParent
,
120 nsIContent
* aBindingParent
)
122 Link::ResetLinkState(false, Link::ElementHasHref());
124 nsresult rv
= nsGenericHTMLElement::BindToTree(aDocument
, aParent
,
126 NS_ENSURE_SUCCESS(rv
, rv
);
129 nsIDocument
* doc
= GetComposedDoc();
131 doc
->RegisterPendingLinkUpdate(this);
139 HTMLAnchorElement::UnbindFromTree(bool aDeep
, bool aNullParent
)
141 // Cancel any DNS prefetches
142 // Note: Must come before ResetLinkState. If called after, it will recreate
143 // mCachedURI based on data that is invalid - due to a call to GetHostname.
144 CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED
,
145 HTML_ANCHOR_DNS_PREFETCH_REQUESTED
);
147 // If this link is ever reinserted into a document, it might
148 // be under a different xml:base, so forget the cached state now.
149 Link::ResetLinkState(false, Link::ElementHasHref());
151 nsGenericHTMLElement::UnbindFromTree(aDeep
, aNullParent
);
155 IsNodeInEditableRegion(nsINode
* aNode
)
158 if (aNode
->IsEditable()) {
161 aNode
= aNode
->GetParent();
167 HTMLAnchorElement::IsHTMLFocusable(bool aWithMouse
,
168 bool *aIsFocusable
, int32_t *aTabIndex
)
170 if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse
, aIsFocusable
, aTabIndex
)) {
174 // cannot focus links if there is no link handler
175 nsIDocument
* doc
= GetComposedDoc();
177 nsPresContext
* presContext
= doc
->GetPresContext();
178 if (presContext
&& !presContext
->GetLinkHandler()) {
179 *aIsFocusable
= false;
184 // Links that are in an editable region should never be focusable, even if
185 // they are in a contenteditable="false" region.
186 if (IsNodeInEditableRegion(this)) {
191 *aIsFocusable
= false;
196 if (!HasAttr(kNameSpaceID_None
, nsGkAtoms::tabindex
)) {
197 // check whether we're actually a link
198 if (!Link::HasURI()) {
199 // Not tabbable or focusable without href (bug 17605), unless
200 // forced to be via presence of nonnegative tabindex attribute
205 *aIsFocusable
= false;
211 if (aTabIndex
&& (sTabFocusModel
& eTabFocus_linksMask
) == 0) {
215 *aIsFocusable
= true;
221 HTMLAnchorElement::GetEventTargetParent(EventChainPreVisitor
& aVisitor
)
223 GetEventTargetParentForAnchors(aVisitor
);
227 HTMLAnchorElement::PostHandleEvent(EventChainPostVisitor
& aVisitor
)
229 return PostHandleEventForAnchors(aVisitor
);
233 HTMLAnchorElement::IsLink(nsIURI
** aURI
) const
235 return IsHTMLLink(aURI
);
239 HTMLAnchorElement::GetLinkTarget(nsAString
& aTarget
)
241 GetAttr(kNameSpaceID_None
, nsGkAtoms::target
, aTarget
);
242 if (aTarget
.IsEmpty()) {
243 GetBaseTarget(aTarget
);
248 HTMLAnchorElement::GetTarget(nsAString
& aValue
)
250 if (!GetAttr(kNameSpaceID_None
, nsGkAtoms::target
, aValue
)) {
251 GetBaseTarget(aValue
);
256 HTMLAnchorElement::RelList()
259 mRelList
= new nsDOMTokenList(this, nsGkAtoms::rel
, sSupportedRelValues
);
265 HTMLAnchorElement::GetText(nsAString
& aText
, mozilla::ErrorResult
& aRv
)
267 if (NS_WARN_IF(!nsContentUtils::GetNodeTextContent(this, true, aText
, fallible
))) {
268 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
273 HTMLAnchorElement::SetText(const nsAString
& aText
, ErrorResult
& aRv
)
275 aRv
= nsContentUtils::SetNodeTextContent(this, aText
, false);
279 HTMLAnchorElement::ToString(nsAString
& aSource
)
281 return GetHref(aSource
);
284 already_AddRefed
<nsIURI
>
285 HTMLAnchorElement::GetHrefURI() const
287 nsCOMPtr
<nsIURI
> uri
= Link::GetCachedURI();
292 return GetHrefURIForAnchors();
296 HTMLAnchorElement::BeforeSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
297 const nsAttrValueOrString
* aValue
,
300 if (aNamespaceID
== kNameSpaceID_None
) {
301 if (aName
== nsGkAtoms::href
) {
302 CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED
,
303 HTML_ANCHOR_DNS_PREFETCH_REQUESTED
);
307 return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID
, aName
, aValue
,
312 HTMLAnchorElement::AfterSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
313 const nsAttrValue
* aValue
,
314 const nsAttrValue
* aOldValue
,
315 nsIPrincipal
* aSubjectPrincipal
,
318 if (aNamespaceID
== kNameSpaceID_None
) {
319 if (aName
== nsGkAtoms::href
) {
320 Link::ResetLinkState(aNotify
, !!aValue
);
321 if (aValue
&& IsInComposedDoc()) {
327 return nsGenericHTMLElement::AfterSetAttr(aNamespaceID
, aName
,
328 aValue
, aOldValue
, aSubjectPrincipal
, aNotify
);
332 HTMLAnchorElement::IntrinsicState() const
334 return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
338 HTMLAnchorElement::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
339 size_t* aNodeSize
) const
341 nsGenericHTMLElement::AddSizeOfExcludingThis(aSizes
, aNodeSize
);
342 *aNodeSize
+= Link::SizeOfExcludingThis(aSizes
.mState
);
346 } // namespace mozilla