HTMLAnchorElement.cpp (7184B)
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/EventDispatcher.h" 10 #include "mozilla/FocusModel.h" 11 #include "mozilla/dom/BindContext.h" 12 #include "mozilla/dom/Document.h" 13 #include "mozilla/dom/HTMLAnchorElementBinding.h" 14 #include "mozilla/dom/HTMLDNSPrefetch.h" 15 #include "nsCOMPtr.h" 16 #include "nsContentUtils.h" 17 #include "nsGkAtoms.h" 18 #include "nsIURI.h" 19 #include "nsPresContext.h" 20 #include "nsWindowSizes.h" 21 22 NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor) 23 24 namespace mozilla::dom { 25 26 HTMLAnchorElement::~HTMLAnchorElement() { 27 SupportsDNSPrefetch::Destroyed(*this); 28 } 29 30 bool HTMLAnchorElement::IsInteractiveHTMLContent() const { 31 return HasAttr(nsGkAtoms::href) || 32 nsGenericHTMLElement::IsInteractiveHTMLContent(); 33 } 34 35 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLAnchorElement, 36 nsGenericHTMLElement, Link) 37 38 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLAnchorElement, nsGenericHTMLElement, 39 mRelList) 40 41 NS_IMPL_ELEMENT_CLONE(HTMLAnchorElement) 42 43 JSObject* HTMLAnchorElement::WrapNode(JSContext* aCx, 44 JS::Handle<JSObject*> aGivenProto) { 45 return HTMLAnchorElement_Binding::Wrap(aCx, this, aGivenProto); 46 } 47 48 int32_t HTMLAnchorElement::TabIndexDefault() { return 0; } 49 50 bool HTMLAnchorElement::Draggable() const { 51 // links can be dragged as long as there is an href and the 52 // draggable attribute isn't false 53 if (!HasAttr(nsGkAtoms::href)) { 54 // no href, so just use the same behavior as other elements 55 return nsGenericHTMLElement::Draggable(); 56 } 57 58 return !AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable, 59 nsGkAtoms::_false, eIgnoreCase); 60 } 61 62 nsresult HTMLAnchorElement::BindToTree(BindContext& aContext, 63 nsINode& aParent) { 64 nsresult rv = nsGenericHTMLElement::BindToTree(aContext, aParent); 65 NS_ENSURE_SUCCESS(rv, rv); 66 67 Link::BindToTree(aContext); 68 69 // Prefetch links 70 MaybeTryDNSPrefetch(); 71 return rv; 72 } 73 74 void HTMLAnchorElement::UnbindFromTree(UnbindContext& aContext) { 75 // Cancel any DNS prefetches 76 // Note: Must come before ResetLinkState. If called after, it will recreate 77 // mCachedURI based on data that is invalid - due to a call to Link::GetURI() 78 // via GetURIForDNSPrefetch(). 79 CancelDNSPrefetch(*this); 80 81 nsGenericHTMLElement::UnbindFromTree(aContext); 82 83 // Without removing the link state we risk a dangling pointer in the 84 // mStyledLinks hashtable 85 Link::UnbindFromTree(); 86 } 87 88 bool HTMLAnchorElement::IsHTMLFocusable(IsFocusableFlags aFlags, 89 bool* aIsFocusable, 90 int32_t* aTabIndex) { 91 if (nsGenericHTMLElement::IsHTMLFocusable(aFlags, aIsFocusable, aTabIndex)) { 92 return true; 93 } 94 95 // cannot focus links if there is no link handler 96 if (!OwnerDoc()->LinkHandlingEnabled()) { 97 *aTabIndex = -1; 98 *aIsFocusable = false; 99 return false; 100 } 101 102 // Links that are in an editable region should never be focusable, even if 103 // they are in a contenteditable="false" region. 104 if (nsContentUtils::IsNodeInEditableRegion(this)) { 105 *aTabIndex = -1; 106 *aIsFocusable = false; 107 return true; 108 } 109 110 if (GetTabIndexAttrValue().isNothing()) { 111 // check whether we're actually a link 112 if (!IsLink()) { 113 // Not tabbable or focusable without href (bug 17605), unless 114 // forced to be via presence of nonnegative tabindex attribute 115 *aTabIndex = -1; 116 *aIsFocusable = false; 117 return false; 118 } 119 } 120 121 if (!FocusModel::IsTabFocusable(TabFocusableType::Links)) { 122 *aTabIndex = -1; 123 } 124 *aIsFocusable = true; 125 return false; 126 } 127 128 void HTMLAnchorElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { 129 GetEventTargetParentForAnchors(aVisitor); 130 } 131 132 nsresult HTMLAnchorElement::PostHandleEvent(EventChainPostVisitor& aVisitor) { 133 return PostHandleEventForAnchors(aVisitor); 134 } 135 136 void HTMLAnchorElement::GetLinkTargetImpl(nsAString& aTarget) { 137 GetAttr(nsGkAtoms::target, aTarget); 138 if (aTarget.IsEmpty()) { 139 GetBaseTarget(aTarget); 140 } 141 } 142 143 void HTMLAnchorElement::GetTarget(nsAString& aValue) const { 144 if (!GetAttr(nsGkAtoms::target, aValue)) { 145 GetBaseTarget(aValue); 146 } 147 } 148 149 nsDOMTokenList* HTMLAnchorElement::RelList() { 150 if (!mRelList) { 151 mRelList = 152 new nsDOMTokenList(this, nsGkAtoms::rel, sAnchorAndFormRelValues); 153 } 154 return mRelList; 155 } 156 157 void HTMLAnchorElement::GetText(nsAString& aText, 158 mozilla::ErrorResult& aRv) const { 159 if (NS_WARN_IF( 160 !nsContentUtils::GetNodeTextContent(this, true, aText, fallible))) { 161 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 162 } 163 } 164 165 void HTMLAnchorElement::SetText(const nsAString& aText, ErrorResult& aRv) { 166 aRv = nsContentUtils::SetNodeTextContent(this, aText, false); 167 } 168 169 already_AddRefed<nsIURI> HTMLAnchorElement::GetHrefURI() const { 170 if (nsCOMPtr<nsIURI> uri = GetCachedURI()) { 171 return uri.forget(); 172 } 173 return GetHrefURIForAnchors(); 174 } 175 176 void HTMLAnchorElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName, 177 const nsAttrValue* aValue, bool aNotify) { 178 if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::href) { 179 CancelDNSPrefetch(*this); 180 } 181 return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, aValue, 182 aNotify); 183 } 184 185 void HTMLAnchorElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, 186 const nsAttrValue* aValue, 187 const nsAttrValue* aOldValue, 188 nsIPrincipal* aSubjectPrincipal, 189 bool aNotify) { 190 if (aNamespaceID == kNameSpaceID_None) { 191 if (aName == nsGkAtoms::href) { 192 Link::ResetLinkState(aNotify, !!aValue); 193 if (aValue) { 194 MaybeTryDNSPrefetch(); 195 } 196 } 197 } 198 199 return nsGenericHTMLElement::AfterSetAttr( 200 aNamespaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify); 201 } 202 203 void HTMLAnchorElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes, 204 size_t* aNodeSize) const { 205 nsGenericHTMLElement::AddSizeOfExcludingThis(aSizes, aNodeSize); 206 *aNodeSize += Link::SizeOfExcludingThis(aSizes.mState); 207 } 208 209 void HTMLAnchorElement::MaybeTryDNSPrefetch() { 210 if (IsInComposedDoc()) { 211 nsIURI* docURI = OwnerDoc()->GetDocumentURI(); 212 if (!docURI) { 213 return; 214 } 215 216 bool docIsHttps = docURI->SchemeIs("https"); 217 if ((docIsHttps && 218 StaticPrefs::dom_prefetch_dns_for_anchor_https_document()) || 219 (!docIsHttps && 220 StaticPrefs::dom_prefetch_dns_for_anchor_http_document())) { 221 TryDNSPrefetch( 222 *this, HTMLDNSPrefetch::PrefetchSource::AnchorSpeculativePrefetch); 223 } 224 } 225 } 226 227 } // namespace mozilla::dom