1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set tw=80 expandtab softtabstop=2 ts=2 sw=2: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla Communicator client code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Daniel Glazman <glazman@netscape.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "nsGenericHTMLElement.h"
41 : #include "nsIDOMHTMLAnchorElement.h"
42 : #include "nsILink.h"
43 : #include "Link.h"
44 :
45 : #include "nsCOMPtr.h"
46 : #include "nsContentUtils.h"
47 : #include "nsReadableUtils.h"
48 : #include "nsUnicharUtils.h"
49 : #include "nsGkAtoms.h"
50 : #include "nsIPresShell.h"
51 : #include "nsIDocument.h"
52 : #include "nsPresContext.h"
53 : #include "nsHTMLDNSPrefetch.h"
54 :
55 : using namespace mozilla::dom;
56 :
57 : class nsHTMLAnchorElement : public nsGenericHTMLElement,
58 : public nsIDOMHTMLAnchorElement,
59 : public nsILink,
60 : public Link
61 : {
62 : public:
63 : using nsGenericElement::GetText;
64 : using nsGenericElement::SetText;
65 :
66 : nsHTMLAnchorElement(already_AddRefed<nsINodeInfo> aNodeInfo);
67 : virtual ~nsHTMLAnchorElement();
68 :
69 : // nsISupports
70 : NS_DECL_ISUPPORTS_INHERITED
71 :
72 : // nsIDOMNode
73 0 : NS_FORWARD_NSIDOMNODE(nsGenericHTMLElement::)
74 :
75 : // nsIDOMElement
76 0 : NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLElement::)
77 :
78 : // nsIDOMHTMLElement
79 0 : NS_FORWARD_NSIDOMHTMLELEMENT_BASIC(nsGenericHTMLElement::)
80 0 : NS_SCRIPTABLE NS_IMETHOD Click() {
81 0 : return nsGenericHTMLElement::Click();
82 : }
83 : NS_SCRIPTABLE NS_IMETHOD GetTabIndex(PRInt32* aTabIndex);
84 : NS_SCRIPTABLE NS_IMETHOD SetTabIndex(PRInt32 aTabIndex);
85 0 : NS_SCRIPTABLE NS_IMETHOD Focus() {
86 0 : return nsGenericHTMLElement::Focus();
87 : }
88 : NS_SCRIPTABLE NS_IMETHOD GetDraggable(bool* aDraggable);
89 0 : NS_SCRIPTABLE NS_IMETHOD GetInnerHTML(nsAString& aInnerHTML) {
90 0 : return nsGenericHTMLElement::GetInnerHTML(aInnerHTML);
91 : }
92 0 : NS_SCRIPTABLE NS_IMETHOD SetInnerHTML(const nsAString& aInnerHTML) {
93 0 : return nsGenericHTMLElement::SetInnerHTML(aInnerHTML);
94 : }
95 :
96 : // nsIDOMHTMLAnchorElement
97 : NS_DECL_NSIDOMHTMLANCHORELEMENT
98 :
99 : // DOM memory reporter participant
100 : NS_DECL_SIZEOF_EXCLUDING_THIS
101 :
102 : // nsILink
103 0 : NS_IMETHOD LinkAdded() { return NS_OK; }
104 0 : NS_IMETHOD LinkRemoved() { return NS_OK; }
105 :
106 : virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
107 : nsIContent* aBindingParent,
108 : bool aCompileEventHandlers);
109 : virtual void UnbindFromTree(bool aDeep = true,
110 : bool aNullParent = true);
111 : virtual bool IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, PRInt32 *aTabIndex);
112 :
113 : virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
114 : virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
115 : virtual bool IsLink(nsIURI** aURI) const;
116 : virtual void GetLinkTarget(nsAString& aTarget);
117 : virtual nsLinkState GetLinkState() const;
118 : virtual already_AddRefed<nsIURI> GetHrefURI() const;
119 :
120 0 : nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
121 : const nsAString& aValue, bool aNotify)
122 : {
123 0 : return SetAttr(aNameSpaceID, aName, nsnull, aValue, aNotify);
124 : }
125 : virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
126 : nsIAtom* aPrefix, const nsAString& aValue,
127 : bool aNotify);
128 : virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
129 : bool aNotify);
130 : virtual bool ParseAttribute(PRInt32 aNamespaceID,
131 : nsIAtom* aAttribute,
132 : const nsAString& aValue,
133 : nsAttrValue& aResult);
134 :
135 : virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
136 :
137 : virtual nsEventStates IntrinsicState() const;
138 :
139 : virtual nsXPCClassInfo* GetClassInfo();
140 :
141 : virtual void OnDNSPrefetchDeferred();
142 : virtual void OnDNSPrefetchRequested();
143 : virtual bool HasDeferredDNSPrefetchRequest();
144 : };
145 :
146 : // Indicates that a DNS Prefetch has been requested from this Anchor elem
147 : #define HTML_ANCHOR_DNS_PREFETCH_REQUESTED \
148 : (1 << ELEMENT_TYPE_SPECIFIC_BITS_OFFSET)
149 : // Indicates that a DNS Prefetch was added to the deferral queue
150 : #define HTML_ANCHOR_DNS_PREFETCH_DEFERRED \
151 : (1 << (ELEMENT_TYPE_SPECIFIC_BITS_OFFSET+1))
152 :
153 : // Make sure we have enough space for those bits
154 : PR_STATIC_ASSERT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET+1 < 32);
155 :
156 0 : NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor)
157 :
158 0 : nsHTMLAnchorElement::nsHTMLAnchorElement(already_AddRefed<nsINodeInfo> aNodeInfo)
159 : : nsGenericHTMLElement(aNodeInfo)
160 0 : , Link(this)
161 : {
162 0 : }
163 :
164 0 : nsHTMLAnchorElement::~nsHTMLAnchorElement()
165 : {
166 0 : }
167 :
168 :
169 0 : NS_IMPL_ADDREF_INHERITED(nsHTMLAnchorElement, nsGenericElement)
170 0 : NS_IMPL_RELEASE_INHERITED(nsHTMLAnchorElement, nsGenericElement)
171 :
172 :
173 0 : DOMCI_NODE_DATA(HTMLAnchorElement, nsHTMLAnchorElement)
174 :
175 : // QueryInterface implementation for nsHTMLAnchorElement
176 0 : NS_INTERFACE_TABLE_HEAD(nsHTMLAnchorElement)
177 0 : NS_HTML_CONTENT_INTERFACE_TABLE3(nsHTMLAnchorElement,
178 : nsIDOMHTMLAnchorElement,
179 : nsILink,
180 : Link)
181 0 : NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLAnchorElement,
182 : nsGenericHTMLElement)
183 0 : NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLAnchorElement)
184 :
185 :
186 0 : NS_IMPL_ELEMENT_CLONE(nsHTMLAnchorElement)
187 :
188 :
189 0 : NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Charset, charset)
190 0 : NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Coords, coords)
191 0 : NS_IMPL_URI_ATTR(nsHTMLAnchorElement, Href, href)
192 0 : NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Hreflang, hreflang)
193 0 : NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Name, name)
194 0 : NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Rel, rel)
195 0 : NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Rev, rev)
196 0 : NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Shape, shape)
197 0 : NS_IMPL_INT_ATTR(nsHTMLAnchorElement, TabIndex, tabindex)
198 0 : NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Type, type)
199 :
200 : NS_IMETHODIMP
201 0 : nsHTMLAnchorElement::GetDraggable(bool* aDraggable)
202 : {
203 : // links can be dragged as long as there is an href and the
204 : // draggable attribute isn't false
205 0 : if (HasAttr(kNameSpaceID_None, nsGkAtoms::href)) {
206 : *aDraggable = !AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
207 0 : nsGkAtoms::_false, eIgnoreCase);
208 0 : return NS_OK;
209 : }
210 :
211 : // no href, so just use the same behavior as other elements
212 0 : return nsGenericHTMLElement::GetDraggable(aDraggable);
213 : }
214 :
215 : void
216 0 : nsHTMLAnchorElement::OnDNSPrefetchRequested()
217 : {
218 0 : UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED);
219 0 : SetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
220 0 : }
221 :
222 : void
223 0 : nsHTMLAnchorElement::OnDNSPrefetchDeferred()
224 : {
225 0 : UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
226 0 : SetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED);
227 0 : }
228 :
229 : bool
230 0 : nsHTMLAnchorElement::HasDeferredDNSPrefetchRequest()
231 : {
232 0 : return HasFlag(HTML_ANCHOR_DNS_PREFETCH_DEFERRED);
233 : }
234 :
235 : nsresult
236 0 : nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
237 : nsIContent* aBindingParent,
238 : bool aCompileEventHandlers)
239 : {
240 0 : Link::ResetLinkState(false);
241 :
242 : nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
243 : aBindingParent,
244 0 : aCompileEventHandlers);
245 0 : NS_ENSURE_SUCCESS(rv, rv);
246 :
247 : // Prefetch links
248 0 : if (aDocument) {
249 0 : aDocument->RegisterPendingLinkUpdate(this);
250 0 : if (nsHTMLDNSPrefetch::IsAllowed(OwnerDoc())) {
251 0 : nsHTMLDNSPrefetch::PrefetchLow(this);
252 : }
253 : }
254 :
255 0 : return rv;
256 : }
257 :
258 : void
259 0 : nsHTMLAnchorElement::UnbindFromTree(bool aDeep, bool aNullParent)
260 : {
261 : // Cancel any DNS prefetches
262 : // Note: Must come before ResetLinkState. If called after, it will recreate
263 : // mCachedURI based on data that is invalid - due to a call to GetHostname.
264 :
265 : // If prefetch was deferred, clear flag and move on
266 0 : if (HasFlag(HTML_ANCHOR_DNS_PREFETCH_DEFERRED))
267 0 : UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED);
268 : // Else if prefetch was requested, clear flag and send cancellation
269 0 : else if (HasFlag(HTML_ANCHOR_DNS_PREFETCH_REQUESTED)) {
270 0 : UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
271 : // Possible that hostname could have changed since binding, but since this
272 : // covers common cases, most DNS prefetch requests will be canceled
273 0 : nsHTMLDNSPrefetch::CancelPrefetchLow(this, NS_ERROR_ABORT);
274 : }
275 :
276 : // If this link is ever reinserted into a document, it might
277 : // be under a different xml:base, so forget the cached state now.
278 0 : Link::ResetLinkState(false);
279 :
280 0 : nsIDocument* doc = GetCurrentDoc();
281 0 : if (doc) {
282 0 : doc->UnregisterPendingLinkUpdate(this);
283 : }
284 :
285 0 : nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
286 0 : }
287 :
288 : bool
289 0 : nsHTMLAnchorElement::IsHTMLFocusable(bool aWithMouse,
290 : bool *aIsFocusable, PRInt32 *aTabIndex)
291 : {
292 0 : if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) {
293 0 : return true;
294 : }
295 :
296 : // cannot focus links if there is no link handler
297 0 : nsIDocument* doc = GetCurrentDoc();
298 0 : if (doc) {
299 0 : nsIPresShell* presShell = doc->GetShell();
300 0 : if (presShell) {
301 0 : nsPresContext* presContext = presShell->GetPresContext();
302 0 : if (presContext && !presContext->GetLinkHandler()) {
303 0 : *aIsFocusable = false;
304 0 : return false;
305 : }
306 : }
307 : }
308 :
309 0 : if (IsEditable()) {
310 0 : if (aTabIndex) {
311 0 : *aTabIndex = -1;
312 : }
313 :
314 0 : *aIsFocusable = false;
315 :
316 0 : return true;
317 : }
318 :
319 0 : if (!HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
320 : // check whether we're actually a link
321 0 : nsCOMPtr<nsIURI> absURI;
322 0 : if (!IsLink(getter_AddRefs(absURI))) {
323 : // Not tabbable or focusable without href (bug 17605), unless
324 : // forced to be via presence of nonnegative tabindex attribute
325 0 : if (aTabIndex) {
326 0 : *aTabIndex = -1;
327 : }
328 :
329 0 : *aIsFocusable = false;
330 :
331 0 : return false;
332 : }
333 : }
334 :
335 0 : if (aTabIndex && (sTabFocusModel & eTabFocus_linksMask) == 0) {
336 0 : *aTabIndex = -1;
337 : }
338 :
339 0 : *aIsFocusable = true;
340 :
341 0 : return false;
342 : }
343 :
344 : nsresult
345 0 : nsHTMLAnchorElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
346 : {
347 0 : return PreHandleEventForAnchors(aVisitor);
348 : }
349 :
350 : nsresult
351 0 : nsHTMLAnchorElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
352 : {
353 0 : return PostHandleEventForAnchors(aVisitor);
354 : }
355 :
356 : bool
357 0 : nsHTMLAnchorElement::IsLink(nsIURI** aURI) const
358 : {
359 0 : return IsHTMLLink(aURI);
360 : }
361 :
362 : void
363 0 : nsHTMLAnchorElement::GetLinkTarget(nsAString& aTarget)
364 : {
365 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::target, aTarget);
366 0 : if (aTarget.IsEmpty()) {
367 0 : GetBaseTarget(aTarget);
368 : }
369 0 : }
370 :
371 : NS_IMETHODIMP
372 0 : nsHTMLAnchorElement::GetTarget(nsAString& aValue)
373 : {
374 0 : if (!GetAttr(kNameSpaceID_None, nsGkAtoms::target, aValue)) {
375 0 : GetBaseTarget(aValue);
376 : }
377 0 : return NS_OK;
378 : }
379 :
380 : NS_IMETHODIMP
381 0 : nsHTMLAnchorElement::SetTarget(const nsAString& aValue)
382 : {
383 0 : return SetAttr(kNameSpaceID_None, nsGkAtoms::target, aValue, true);
384 : }
385 :
386 : #define IMPL_URI_PART(_part) \
387 : NS_IMETHODIMP \
388 : nsHTMLAnchorElement::Get##_part(nsAString& a##_part) \
389 : { \
390 : return Link::Get##_part(a##_part); \
391 : } \
392 : NS_IMETHODIMP \
393 : nsHTMLAnchorElement::Set##_part(const nsAString& a##_part) \
394 : { \
395 : return Link::Set##_part(a##_part); \
396 : }
397 :
398 0 : IMPL_URI_PART(Protocol)
399 0 : IMPL_URI_PART(Host)
400 0 : IMPL_URI_PART(Hostname)
401 0 : IMPL_URI_PART(Pathname)
402 0 : IMPL_URI_PART(Search)
403 0 : IMPL_URI_PART(Port)
404 0 : IMPL_URI_PART(Hash)
405 :
406 : #undef IMPL_URI_PART
407 :
408 : NS_IMETHODIMP
409 0 : nsHTMLAnchorElement::GetText(nsAString& aText)
410 : {
411 0 : nsContentUtils::GetNodeTextContent(this, true, aText);
412 0 : return NS_OK;
413 : }
414 :
415 : NS_IMETHODIMP
416 0 : nsHTMLAnchorElement::SetText(const nsAString& aText)
417 : {
418 0 : return nsContentUtils::SetNodeTextContent(this, aText, false);
419 : }
420 :
421 : NS_IMETHODIMP
422 0 : nsHTMLAnchorElement::ToString(nsAString& aSource)
423 : {
424 0 : return GetHref(aSource);
425 : }
426 :
427 : NS_IMETHODIMP
428 0 : nsHTMLAnchorElement::GetPing(nsAString& aValue)
429 : {
430 0 : return GetURIListAttr(nsGkAtoms::ping, aValue);
431 : }
432 :
433 : NS_IMETHODIMP
434 0 : nsHTMLAnchorElement::SetPing(const nsAString& aValue)
435 : {
436 0 : return SetAttr(kNameSpaceID_None, nsGkAtoms::ping, aValue, true);
437 : }
438 :
439 : nsLinkState
440 0 : nsHTMLAnchorElement::GetLinkState() const
441 : {
442 0 : return Link::GetLinkState();
443 : }
444 :
445 : already_AddRefed<nsIURI>
446 0 : nsHTMLAnchorElement::GetHrefURI() const
447 : {
448 0 : return GetHrefURIForAnchors();
449 : }
450 :
451 : nsresult
452 0 : nsHTMLAnchorElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
453 : nsIAtom* aPrefix, const nsAString& aValue,
454 : bool aNotify)
455 : {
456 0 : bool reset = false;
457 0 : if (aName == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
458 : // If we do not have a cached URI, we have some value here so we must reset
459 : // our link state after calling the parent.
460 0 : if (!Link::HasCachedURI()) {
461 0 : reset = true;
462 : }
463 : // However, if we have a cached URI, we'll want to see if the value changed.
464 : else {
465 0 : nsAutoString val;
466 0 : GetHref(val);
467 0 : if (!val.Equals(aValue)) {
468 0 : reset = true;
469 : }
470 : }
471 : }
472 :
473 : nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix,
474 0 : aValue, aNotify);
475 :
476 : // The ordering of the parent class's SetAttr call and Link::ResetLinkState
477 : // is important here! The attribute is not set until SetAttr returns, and
478 : // we will need the updated attribute value because notifying the document
479 : // that content states have changed will call IntrinsicState, which will try
480 : // to get updated information about the visitedness from Link.
481 0 : if (reset) {
482 0 : Link::ResetLinkState(!!aNotify);
483 : }
484 :
485 0 : return rv;
486 : }
487 :
488 : nsresult
489 0 : nsHTMLAnchorElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
490 : bool aNotify)
491 : {
492 : nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute,
493 0 : aNotify);
494 :
495 : // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
496 : // is important here! The attribute is not unset until UnsetAttr returns, and
497 : // we will need the updated attribute value because notifying the document
498 : // that content states have changed will call IntrinsicState, which will try
499 : // to get updated information about the visitedness from Link.
500 0 : if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
501 0 : Link::ResetLinkState(!!aNotify);
502 : }
503 :
504 0 : return rv;
505 : }
506 :
507 : bool
508 0 : nsHTMLAnchorElement::ParseAttribute(PRInt32 aNamespaceID,
509 : nsIAtom* aAttribute,
510 : const nsAString& aValue,
511 : nsAttrValue& aResult)
512 : {
513 : return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
514 0 : aResult);
515 : }
516 :
517 : nsEventStates
518 0 : nsHTMLAnchorElement::IntrinsicState() const
519 : {
520 0 : return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
521 : }
522 :
523 : size_t
524 0 : nsHTMLAnchorElement::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
525 : {
526 0 : return nsGenericHTMLElement::SizeOfExcludingThis(aMallocSizeOf) +
527 0 : Link::SizeOfExcludingThis(aMallocSizeOf);
528 : }
529 :
|