Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/html/HTMLAnchorElement.cpp
Line
Count
Source (jump to first uncovered line)
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
#include "nsWindowSizes.h"
23
24
NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor)
25
26
namespace mozilla {
27
namespace dom {
28
29
#define ANCHOR_ELEMENT_FLAG_BIT(n_) NODE_FLAG_BIT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + (n_))
30
31
// Anchor element specific bits
32
enum {
33
  // Indicates that a DNS Prefetch has been requested from this Anchor elem
34
  HTML_ANCHOR_DNS_PREFETCH_REQUESTED =    ANCHOR_ELEMENT_FLAG_BIT(0),
35
36
  // Indicates that a DNS Prefetch was added to the deferral queue
37
  HTML_ANCHOR_DNS_PREFETCH_DEFERRED =     ANCHOR_ELEMENT_FLAG_BIT(1)
38
};
39
40
ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2);
41
42
#undef ANCHOR_ELEMENT_FLAG_BIT
43
44
// static
45
const DOMTokenListSupportedToken HTMLAnchorElement::sSupportedRelValues[] = {
46
  "noreferrer",
47
  "noopener",
48
  nullptr
49
};
50
51
HTMLAnchorElement::~HTMLAnchorElement()
52
0
{
53
0
}
54
55
bool
56
HTMLAnchorElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
57
0
{
58
0
  return HasAttr(kNameSpaceID_None, nsGkAtoms::href) ||
59
0
         nsGenericHTMLElement::IsInteractiveHTMLContent(aIgnoreTabindex);
60
0
}
61
62
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLAnchorElement,
63
                                             nsGenericHTMLElement,
64
                                             Link)
65
66
NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLAnchorElement,
67
                                   nsGenericHTMLElement,
68
                                   mRelList)
69
70
NS_IMPL_ELEMENT_CLONE(HTMLAnchorElement)
71
72
JSObject*
73
HTMLAnchorElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
74
0
{
75
0
  return HTMLAnchorElement_Binding::Wrap(aCx, this, aGivenProto);
76
0
}
77
78
int32_t
79
HTMLAnchorElement::TabIndexDefault()
80
0
{
81
0
  return 0;
82
0
}
83
84
bool
85
HTMLAnchorElement::Draggable() const
86
0
{
87
0
  // links can be dragged as long as there is an href and the
88
0
  // draggable attribute isn't false
89
0
  if (!HasAttr(kNameSpaceID_None, nsGkAtoms::href)) {
90
0
    // no href, so just use the same behavior as other elements
91
0
    return nsGenericHTMLElement::Draggable();
92
0
  }
93
0
94
0
  return !AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
95
0
                      nsGkAtoms::_false, eIgnoreCase);
96
0
}
97
98
void
99
HTMLAnchorElement::OnDNSPrefetchRequested()
100
0
{
101
0
  UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED);
102
0
  SetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
103
0
}
104
105
void
106
HTMLAnchorElement::OnDNSPrefetchDeferred()
107
0
{
108
0
  UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
109
0
  SetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED);
110
0
}
111
112
bool
113
HTMLAnchorElement::HasDeferredDNSPrefetchRequest()
114
0
{
115
0
  return HasFlag(HTML_ANCHOR_DNS_PREFETCH_DEFERRED);
116
0
}
117
118
nsresult
119
HTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
120
                              nsIContent* aBindingParent)
121
0
{
122
0
  Link::ResetLinkState(false, Link::ElementHasHref());
123
0
124
0
  nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
125
0
                                                 aBindingParent);
126
0
  NS_ENSURE_SUCCESS(rv, rv);
127
0
128
0
  // Prefetch links
129
0
  nsIDocument* doc = GetComposedDoc();
130
0
  if (doc) {
131
0
    doc->RegisterPendingLinkUpdate(this);
132
0
    TryDNSPrefetch();
133
0
  }
134
0
135
0
  return rv;
136
0
}
137
138
void
139
HTMLAnchorElement::UnbindFromTree(bool aDeep, bool aNullParent)
140
0
{
141
0
  // Cancel any DNS prefetches
142
0
  // Note: Must come before ResetLinkState.  If called after, it will recreate
143
0
  // mCachedURI based on data that is invalid - due to a call to GetHostname.
144
0
  CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED,
145
0
                    HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
146
0
147
0
  // If this link is ever reinserted into a document, it might
148
0
  // be under a different xml:base, so forget the cached state now.
149
0
  Link::ResetLinkState(false, Link::ElementHasHref());
150
0
151
0
  nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
152
0
}
153
154
static bool
155
IsNodeInEditableRegion(nsINode* aNode)
156
0
{
157
0
  while (aNode) {
158
0
    if (aNode->IsEditable()) {
159
0
      return true;
160
0
    }
161
0
    aNode = aNode->GetParent();
162
0
  }
163
0
  return false;
164
0
}
165
166
bool
167
HTMLAnchorElement::IsHTMLFocusable(bool aWithMouse,
168
                                   bool *aIsFocusable, int32_t *aTabIndex)
169
0
{
170
0
  if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) {
171
0
    return true;
172
0
  }
173
0
174
0
  // cannot focus links if there is no link handler
175
0
  nsIDocument* doc = GetComposedDoc();
176
0
  if (doc) {
177
0
    nsPresContext* presContext = doc->GetPresContext();
178
0
    if (presContext && !presContext->GetLinkHandler()) {
179
0
      *aIsFocusable = false;
180
0
      return false;
181
0
    }
182
0
  }
183
0
184
0
  // Links that are in an editable region should never be focusable, even if
185
0
  // they are in a contenteditable="false" region.
186
0
  if (IsNodeInEditableRegion(this)) {
187
0
    if (aTabIndex) {
188
0
      *aTabIndex = -1;
189
0
    }
190
0
191
0
    *aIsFocusable = false;
192
0
193
0
    return true;
194
0
  }
195
0
196
0
  if (!HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
197
0
    // check whether we're actually a link
198
0
    if (!Link::HasURI()) {
199
0
      // Not tabbable or focusable without href (bug 17605), unless
200
0
      // forced to be via presence of nonnegative tabindex attribute
201
0
      if (aTabIndex) {
202
0
        *aTabIndex = -1;
203
0
      }
204
0
205
0
      *aIsFocusable = false;
206
0
207
0
      return false;
208
0
    }
209
0
  }
210
0
211
0
  if (aTabIndex && (sTabFocusModel & eTabFocus_linksMask) == 0) {
212
0
    *aTabIndex = -1;
213
0
  }
214
0
215
0
  *aIsFocusable = true;
216
0
217
0
  return false;
218
0
}
219
220
void
221
HTMLAnchorElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
222
0
{
223
0
  GetEventTargetParentForAnchors(aVisitor);
224
0
}
225
226
nsresult
227
HTMLAnchorElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
228
0
{
229
0
  return PostHandleEventForAnchors(aVisitor);
230
0
}
231
232
bool
233
HTMLAnchorElement::IsLink(nsIURI** aURI) const
234
0
{
235
0
  return IsHTMLLink(aURI);
236
0
}
237
238
void
239
HTMLAnchorElement::GetLinkTarget(nsAString& aTarget)
240
0
{
241
0
  GetAttr(kNameSpaceID_None, nsGkAtoms::target, aTarget);
242
0
  if (aTarget.IsEmpty()) {
243
0
    GetBaseTarget(aTarget);
244
0
  }
245
0
}
246
247
void
248
HTMLAnchorElement::GetTarget(nsAString& aValue)
249
0
{
250
0
  if (!GetAttr(kNameSpaceID_None, nsGkAtoms::target, aValue)) {
251
0
    GetBaseTarget(aValue);
252
0
  }
253
0
}
254
255
nsDOMTokenList*
256
HTMLAnchorElement::RelList()
257
0
{
258
0
  if (!mRelList) {
259
0
    mRelList = new nsDOMTokenList(this, nsGkAtoms::rel, sSupportedRelValues);
260
0
  }
261
0
  return mRelList;
262
0
}
263
264
void
265
HTMLAnchorElement::GetText(nsAString& aText, mozilla::ErrorResult& aRv)
266
0
{
267
0
  if (NS_WARN_IF(!nsContentUtils::GetNodeTextContent(this, true, aText, fallible))) {
268
0
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
269
0
  }
270
0
}
271
272
void
273
HTMLAnchorElement::SetText(const nsAString& aText, ErrorResult& aRv)
274
0
{
275
0
  aRv = nsContentUtils::SetNodeTextContent(this, aText, false);
276
0
}
277
278
void
279
HTMLAnchorElement::ToString(nsAString& aSource)
280
0
{
281
0
  return GetHref(aSource);
282
0
}
283
284
already_AddRefed<nsIURI>
285
HTMLAnchorElement::GetHrefURI() const
286
0
{
287
0
  nsCOMPtr<nsIURI> uri = Link::GetCachedURI();
288
0
  if (uri) {
289
0
    return uri.forget();
290
0
  }
291
0
292
0
  return GetHrefURIForAnchors();
293
0
}
294
295
nsresult
296
HTMLAnchorElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
297
                                 const nsAttrValueOrString* aValue,
298
                                 bool aNotify)
299
0
{
300
0
  if (aNamespaceID == kNameSpaceID_None) {
301
0
    if (aName == nsGkAtoms::href) {
302
0
      CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED,
303
0
                        HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
304
0
    }
305
0
  }
306
0
307
0
  return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, aValue,
308
0
                                             aNotify);
309
0
}
310
311
nsresult
312
HTMLAnchorElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
313
                                const nsAttrValue* aValue,
314
                                const nsAttrValue* aOldValue,
315
                                nsIPrincipal* aSubjectPrincipal,
316
                                bool aNotify)
317
0
{
318
0
  if (aNamespaceID == kNameSpaceID_None) {
319
0
    if (aName == nsGkAtoms::href) {
320
0
      Link::ResetLinkState(aNotify, !!aValue);
321
0
      if (aValue && IsInComposedDoc()) {
322
0
        TryDNSPrefetch();
323
0
      }
324
0
    }
325
0
  }
326
0
327
0
  return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName,
328
0
                                            aValue, aOldValue, aSubjectPrincipal, aNotify);
329
0
}
330
331
EventStates
332
HTMLAnchorElement::IntrinsicState() const
333
0
{
334
0
  return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
335
0
}
336
337
void
338
HTMLAnchorElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
339
                                          size_t* aNodeSize) const
340
0
{
341
0
  nsGenericHTMLElement::AddSizeOfExcludingThis(aSizes, aNodeSize);
342
0
  *aNodeSize += Link::SizeOfExcludingThis(aSizes.mState);
343
0
}
344
345
} // namespace dom
346
} // namespace mozilla