Line data Source code
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/HTMLAnchorElementBinding.h"
10 : #include "mozilla/EventDispatcher.h"
11 : #include "mozilla/EventStates.h"
12 : #include "mozilla/MemoryReporting.h"
13 : #include "nsCOMPtr.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"
21 : #include "nsIURI.h"
22 :
23 0 : NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor)
24 :
25 : namespace mozilla {
26 : namespace dom {
27 :
28 : #define ANCHOR_ELEMENT_FLAG_BIT(n_) NODE_FLAG_BIT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + (n_))
29 :
30 : // Anchor element specific bits
31 : enum {
32 : // Indicates that a DNS Prefetch has been requested from this Anchor elem
33 : HTML_ANCHOR_DNS_PREFETCH_REQUESTED = ANCHOR_ELEMENT_FLAG_BIT(0),
34 :
35 : // Indicates that a DNS Prefetch was added to the deferral queue
36 : HTML_ANCHOR_DNS_PREFETCH_DEFERRED = ANCHOR_ELEMENT_FLAG_BIT(1)
37 : };
38 :
39 : ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2);
40 :
41 : #undef ANCHOR_ELEMENT_FLAG_BIT
42 :
43 : // static
44 : const DOMTokenListSupportedToken HTMLAnchorElement::sSupportedRelValues[] = {
45 : "noreferrer",
46 : "noopener",
47 : nullptr
48 : };
49 :
50 0 : HTMLAnchorElement::~HTMLAnchorElement()
51 : {
52 0 : }
53 :
54 : bool
55 0 : HTMLAnchorElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
56 : {
57 0 : return HasAttr(kNameSpaceID_None, nsGkAtoms::href) ||
58 0 : nsGenericHTMLElement::IsInteractiveHTMLContent(aIgnoreTabindex);
59 : }
60 :
61 0 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLAnchorElement)
62 0 : NS_INTERFACE_TABLE_INHERITED(HTMLAnchorElement,
63 : nsIDOMHTMLAnchorElement,
64 : Link)
65 0 : NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLElement)
66 :
67 0 : NS_IMPL_ADDREF_INHERITED(HTMLAnchorElement, Element)
68 0 : NS_IMPL_RELEASE_INHERITED(HTMLAnchorElement, Element)
69 :
70 : NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLAnchorElement)
71 :
72 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLAnchorElement,
73 : nsGenericHTMLElement)
74 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelList)
75 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
76 :
77 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLAnchorElement,
78 : nsGenericHTMLElement)
79 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mRelList)
80 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
81 :
82 0 : NS_IMPL_ELEMENT_CLONE(HTMLAnchorElement)
83 :
84 : JSObject*
85 0 : HTMLAnchorElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
86 : {
87 0 : return HTMLAnchorElementBinding::Wrap(aCx, this, aGivenProto);
88 : }
89 :
90 0 : NS_IMPL_STRING_ATTR(HTMLAnchorElement, Charset, charset)
91 0 : NS_IMPL_STRING_ATTR(HTMLAnchorElement, Coords, coords)
92 0 : NS_IMPL_URI_ATTR(HTMLAnchorElement, Href, href)
93 0 : NS_IMPL_STRING_ATTR(HTMLAnchorElement, Hreflang, hreflang)
94 0 : NS_IMPL_STRING_ATTR(HTMLAnchorElement, Name, name)
95 0 : NS_IMPL_STRING_ATTR(HTMLAnchorElement, Rel, rel)
96 0 : NS_IMPL_STRING_ATTR(HTMLAnchorElement, Rev, rev)
97 0 : NS_IMPL_STRING_ATTR(HTMLAnchorElement, Shape, shape)
98 0 : NS_IMPL_STRING_ATTR(HTMLAnchorElement, Type, type)
99 0 : NS_IMPL_STRING_ATTR(HTMLAnchorElement, Download, download)
100 :
101 : int32_t
102 0 : HTMLAnchorElement::TabIndexDefault()
103 : {
104 0 : return 0;
105 : }
106 :
107 : bool
108 0 : HTMLAnchorElement::Draggable() const
109 : {
110 : // links can be dragged as long as there is an href and the
111 : // draggable attribute isn't false
112 0 : if (!HasAttr(kNameSpaceID_None, nsGkAtoms::href)) {
113 : // no href, so just use the same behavior as other elements
114 0 : return nsGenericHTMLElement::Draggable();
115 : }
116 :
117 0 : return !AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
118 0 : nsGkAtoms::_false, eIgnoreCase);
119 : }
120 :
121 : void
122 0 : HTMLAnchorElement::OnDNSPrefetchRequested()
123 : {
124 0 : UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED);
125 0 : SetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
126 0 : }
127 :
128 : void
129 0 : HTMLAnchorElement::OnDNSPrefetchDeferred()
130 : {
131 0 : UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
132 0 : SetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED);
133 0 : }
134 :
135 : bool
136 0 : HTMLAnchorElement::HasDeferredDNSPrefetchRequest()
137 : {
138 0 : return HasFlag(HTML_ANCHOR_DNS_PREFETCH_DEFERRED);
139 : }
140 :
141 : nsresult
142 0 : HTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
143 : nsIContent* aBindingParent,
144 : bool aCompileEventHandlers)
145 : {
146 0 : Link::ResetLinkState(false, Link::ElementHasHref());
147 :
148 0 : nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
149 : aBindingParent,
150 0 : aCompileEventHandlers);
151 0 : NS_ENSURE_SUCCESS(rv, rv);
152 :
153 : // Prefetch links
154 0 : nsIDocument* doc = GetComposedDoc();
155 0 : if (doc) {
156 0 : doc->RegisterPendingLinkUpdate(this);
157 0 : TryDNSPrefetch();
158 : }
159 :
160 0 : return rv;
161 : }
162 :
163 : void
164 0 : HTMLAnchorElement::UnbindFromTree(bool aDeep, bool aNullParent)
165 : {
166 : // Cancel any DNS prefetches
167 : // Note: Must come before ResetLinkState. If called after, it will recreate
168 : // mCachedURI based on data that is invalid - due to a call to GetHostname.
169 0 : CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED,
170 0 : HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
171 :
172 : // If this link is ever reinserted into a document, it might
173 : // be under a different xml:base, so forget the cached state now.
174 0 : Link::ResetLinkState(false, Link::ElementHasHref());
175 :
176 0 : nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
177 0 : }
178 :
179 : static bool
180 0 : IsNodeInEditableRegion(nsINode* aNode)
181 : {
182 0 : while (aNode) {
183 0 : if (aNode->IsEditable()) {
184 0 : return true;
185 : }
186 0 : aNode = aNode->GetParent();
187 : }
188 0 : return false;
189 : }
190 :
191 : bool
192 0 : HTMLAnchorElement::IsHTMLFocusable(bool aWithMouse,
193 : bool *aIsFocusable, int32_t *aTabIndex)
194 : {
195 0 : if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) {
196 0 : return true;
197 : }
198 :
199 : // cannot focus links if there is no link handler
200 0 : nsIDocument* doc = GetComposedDoc();
201 0 : if (doc) {
202 0 : nsIPresShell* presShell = doc->GetShell();
203 0 : if (presShell) {
204 0 : nsPresContext* presContext = presShell->GetPresContext();
205 0 : if (presContext && !presContext->GetLinkHandler()) {
206 0 : *aIsFocusable = false;
207 0 : return false;
208 : }
209 : }
210 : }
211 :
212 : // Links that are in an editable region should never be focusable, even if
213 : // they are in a contenteditable="false" region.
214 0 : if (IsNodeInEditableRegion(this)) {
215 0 : if (aTabIndex) {
216 0 : *aTabIndex = -1;
217 : }
218 :
219 0 : *aIsFocusable = false;
220 :
221 0 : return true;
222 : }
223 :
224 0 : if (!HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
225 : // check whether we're actually a link
226 0 : if (!Link::HasURI()) {
227 : // Not tabbable or focusable without href (bug 17605), unless
228 : // forced to be via presence of nonnegative tabindex attribute
229 0 : if (aTabIndex) {
230 0 : *aTabIndex = -1;
231 : }
232 :
233 0 : *aIsFocusable = false;
234 :
235 0 : return false;
236 : }
237 : }
238 :
239 0 : if (aTabIndex && (sTabFocusModel & eTabFocus_linksMask) == 0) {
240 0 : *aTabIndex = -1;
241 : }
242 :
243 0 : *aIsFocusable = true;
244 :
245 0 : return false;
246 : }
247 :
248 : nsresult
249 0 : HTMLAnchorElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
250 : {
251 0 : return GetEventTargetParentForAnchors(aVisitor);
252 : }
253 :
254 : nsresult
255 0 : HTMLAnchorElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
256 : {
257 0 : return PostHandleEventForAnchors(aVisitor);
258 : }
259 :
260 : bool
261 0 : HTMLAnchorElement::IsLink(nsIURI** aURI) const
262 : {
263 0 : return IsHTMLLink(aURI);
264 : }
265 :
266 : void
267 0 : HTMLAnchorElement::GetLinkTarget(nsAString& aTarget)
268 : {
269 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::target, aTarget);
270 0 : if (aTarget.IsEmpty()) {
271 0 : GetBaseTarget(aTarget);
272 : }
273 0 : }
274 :
275 : NS_IMETHODIMP
276 0 : HTMLAnchorElement::GetTarget(nsAString& aValue)
277 : {
278 0 : if (!GetAttr(kNameSpaceID_None, nsGkAtoms::target, aValue)) {
279 0 : GetBaseTarget(aValue);
280 : }
281 0 : return NS_OK;
282 : }
283 :
284 : NS_IMETHODIMP
285 0 : HTMLAnchorElement::SetTarget(const nsAString& aValue)
286 : {
287 0 : return SetAttr(kNameSpaceID_None, nsGkAtoms::target, aValue, true);
288 : }
289 :
290 : nsDOMTokenList*
291 0 : HTMLAnchorElement::RelList()
292 : {
293 0 : if (!mRelList) {
294 0 : mRelList = new nsDOMTokenList(this, nsGkAtoms::rel, sSupportedRelValues);
295 : }
296 0 : return mRelList;
297 : }
298 :
299 : #define IMPL_URI_PART(_part) \
300 : NS_IMETHODIMP \
301 : HTMLAnchorElement::Get##_part(nsAString& a##_part) \
302 : { \
303 : Link::Get##_part(a##_part); \
304 : return NS_OK; \
305 : } \
306 : NS_IMETHODIMP \
307 : HTMLAnchorElement::Set##_part(const nsAString& a##_part) \
308 : { \
309 : Link::Set##_part(a##_part); \
310 : return NS_OK; \
311 : }
312 :
313 0 : IMPL_URI_PART(Protocol)
314 0 : IMPL_URI_PART(Host)
315 0 : IMPL_URI_PART(Hostname)
316 0 : IMPL_URI_PART(Pathname)
317 0 : IMPL_URI_PART(Search)
318 0 : IMPL_URI_PART(Port)
319 0 : IMPL_URI_PART(Hash)
320 :
321 : #undef IMPL_URI_PART
322 :
323 : NS_IMETHODIMP
324 0 : HTMLAnchorElement::GetText(nsAString& aText)
325 : {
326 0 : if(!nsContentUtils::GetNodeTextContent(this, true, aText, fallible)) {
327 0 : return NS_ERROR_OUT_OF_MEMORY;
328 : }
329 0 : return NS_OK;
330 : }
331 :
332 : NS_IMETHODIMP
333 0 : HTMLAnchorElement::SetText(const nsAString& aText)
334 : {
335 0 : return nsContentUtils::SetNodeTextContent(this, aText, false);
336 : }
337 :
338 : NS_IMETHODIMP
339 0 : HTMLAnchorElement::ToString(nsAString& aSource)
340 : {
341 0 : return GetHref(aSource);
342 : }
343 :
344 : NS_IMETHODIMP
345 0 : HTMLAnchorElement::GetPing(nsAString& aValue)
346 : {
347 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::ping, aValue);
348 0 : return NS_OK;
349 : }
350 :
351 : NS_IMETHODIMP
352 0 : HTMLAnchorElement::SetPing(const nsAString& aValue)
353 : {
354 0 : return SetAttr(kNameSpaceID_None, nsGkAtoms::ping, aValue, true);
355 : }
356 :
357 : already_AddRefed<nsIURI>
358 0 : HTMLAnchorElement::GetHrefURI() const
359 : {
360 0 : nsCOMPtr<nsIURI> uri = Link::GetCachedURI();
361 0 : if (uri) {
362 0 : return uri.forget();
363 : }
364 :
365 0 : return GetHrefURIForAnchors();
366 : }
367 :
368 : nsresult
369 0 : HTMLAnchorElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
370 : const nsAttrValueOrString* aValue,
371 : bool aNotify)
372 : {
373 0 : if (aNamespaceID == kNameSpaceID_None) {
374 0 : if (aName == nsGkAtoms::href) {
375 0 : CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED,
376 0 : HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
377 : }
378 : }
379 :
380 0 : return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, aValue,
381 0 : aNotify);
382 : }
383 :
384 : nsresult
385 0 : HTMLAnchorElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
386 : const nsAttrValue* aValue,
387 : const nsAttrValue* aOldValue, bool aNotify)
388 : {
389 0 : if (aNamespaceID == kNameSpaceID_None) {
390 0 : if (aName == nsGkAtoms::href) {
391 0 : Link::ResetLinkState(aNotify, !!aValue);
392 0 : if (aValue && IsInComposedDoc()) {
393 0 : TryDNSPrefetch();
394 : }
395 : }
396 : }
397 :
398 0 : return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName,
399 0 : aValue, aOldValue, aNotify);
400 : }
401 :
402 : EventStates
403 0 : HTMLAnchorElement::IntrinsicState() const
404 : {
405 0 : return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
406 : }
407 :
408 : size_t
409 0 : HTMLAnchorElement::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
410 : {
411 0 : return nsGenericHTMLElement::SizeOfExcludingThis(aMallocSizeOf) +
412 0 : Link::SizeOfExcludingThis(aMallocSizeOf);
413 : }
414 :
415 : } // namespace dom
416 : } // namespace mozilla
|