SVGAElement.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "mozilla/dom/SVGAElement.h"
  6. #include "mozilla/Attributes.h"
  7. #include "mozilla/EventDispatcher.h"
  8. #include "mozilla/EventStates.h"
  9. #include "mozilla/dom/SVGAElementBinding.h"
  10. #include "nsCOMPtr.h"
  11. #include "nsContentUtils.h"
  12. #include "nsGkAtoms.h"
  13. #include "nsSVGString.h"
  14. #include "nsIURI.h"
  15. NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(A)
  16. namespace mozilla {
  17. namespace dom {
  18. JSObject*
  19. SVGAElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
  20. {
  21. return SVGAElementBinding::Wrap(aCx, this, aGivenProto);
  22. }
  23. nsSVGElement::StringInfo SVGAElement::sStringInfo[3] =
  24. {
  25. { &nsGkAtoms::href, kNameSpaceID_None, true },
  26. { &nsGkAtoms::href, kNameSpaceID_XLink, true },
  27. { &nsGkAtoms::target, kNameSpaceID_None, true }
  28. };
  29. //----------------------------------------------------------------------
  30. // nsISupports methods
  31. NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(SVGAElement)
  32. NS_INTERFACE_TABLE_INHERITED(SVGAElement,
  33. nsIDOMNode,
  34. nsIDOMElement,
  35. nsIDOMSVGElement,
  36. Link)
  37. NS_INTERFACE_TABLE_TAIL_INHERITING(SVGAElementBase)
  38. NS_IMPL_CYCLE_COLLECTION_CLASS(SVGAElement)
  39. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGAElement,
  40. SVGAElementBase)
  41. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  42. NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGAElement,
  43. SVGAElementBase)
  44. NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  45. NS_IMPL_ADDREF_INHERITED(SVGAElement, SVGAElementBase)
  46. NS_IMPL_RELEASE_INHERITED(SVGAElement, SVGAElementBase)
  47. //----------------------------------------------------------------------
  48. // Implementation
  49. SVGAElement::SVGAElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
  50. : SVGAElementBase(aNodeInfo)
  51. , Link(this)
  52. {
  53. }
  54. SVGAElement::~SVGAElement()
  55. {
  56. }
  57. already_AddRefed<SVGAnimatedString>
  58. SVGAElement::Href()
  59. {
  60. return mStringAttributes[HREF].IsExplicitlySet()
  61. ? mStringAttributes[HREF].ToDOMAnimatedString(this)
  62. : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this);
  63. }
  64. //----------------------------------------------------------------------
  65. // nsINode methods
  66. nsresult
  67. SVGAElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
  68. {
  69. nsresult rv = Element::GetEventTargetParent(aVisitor);
  70. NS_ENSURE_SUCCESS(rv, rv);
  71. return GetEventTargetParentForLinks(aVisitor);
  72. }
  73. nsresult
  74. SVGAElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
  75. {
  76. return PostHandleEventForLinks(aVisitor);
  77. }
  78. NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGAElement)
  79. //----------------------------------------------------------------------
  80. already_AddRefed<SVGAnimatedString>
  81. SVGAElement::Target()
  82. {
  83. return mStringAttributes[TARGET].ToDOMAnimatedString(this);
  84. }
  85. void
  86. SVGAElement::GetDownload(nsAString & aDownload)
  87. {
  88. GetAttr(kNameSpaceID_None, nsGkAtoms::download, aDownload);
  89. }
  90. void
  91. SVGAElement::SetDownload(const nsAString & aDownload, ErrorResult& rv)
  92. {
  93. rv = SetAttr(kNameSpaceID_None, nsGkAtoms::download, aDownload, true);
  94. }
  95. //----------------------------------------------------------------------
  96. // nsIContent methods
  97. nsresult
  98. SVGAElement::BindToTree(nsIDocument *aDocument, nsIContent *aParent,
  99. nsIContent *aBindingParent,
  100. bool aCompileEventHandlers)
  101. {
  102. Link::ResetLinkState(false, Link::ElementHasHref());
  103. nsresult rv = SVGAElementBase::BindToTree(aDocument, aParent,
  104. aBindingParent,
  105. aCompileEventHandlers);
  106. NS_ENSURE_SUCCESS(rv, rv);
  107. nsIDocument* doc = GetComposedDoc();
  108. if (doc) {
  109. doc->RegisterPendingLinkUpdate(this);
  110. }
  111. return NS_OK;
  112. }
  113. void
  114. SVGAElement::UnbindFromTree(bool aDeep, bool aNullParent)
  115. {
  116. // If this link is ever reinserted into a document, it might
  117. // be under a different xml:base, so forget the cached state now.
  118. Link::ResetLinkState(false, Link::ElementHasHref());
  119. // Note, we need to use OwnerDoc() here since GetComposedDoc() may
  120. // return null already at this point.
  121. nsIDocument* doc = OwnerDoc();
  122. if (doc) {
  123. doc->UnregisterPendingLinkUpdate(this);
  124. }
  125. SVGAElementBase::UnbindFromTree(aDeep, aNullParent);
  126. }
  127. already_AddRefed<nsIURI>
  128. SVGAElement::GetHrefURI() const
  129. {
  130. nsCOMPtr<nsIURI> hrefURI;
  131. return IsLink(getter_AddRefs(hrefURI)) ? hrefURI.forget() : nullptr;
  132. }
  133. NS_IMETHODIMP_(bool)
  134. SVGAElement::IsAttributeMapped(const nsIAtom* name) const
  135. {
  136. static const MappedAttributeEntry* const map[] = {
  137. sFEFloodMap,
  138. sFiltersMap,
  139. sFontSpecificationMap,
  140. sGradientStopMap,
  141. sLightingEffectsMap,
  142. sMarkersMap,
  143. sTextContentElementsMap,
  144. sViewportsMap
  145. };
  146. return FindAttributeDependence(name, map) ||
  147. SVGAElementBase::IsAttributeMapped(name);
  148. }
  149. bool
  150. SVGAElement::IsFocusableInternal(int32_t *aTabIndex, bool aWithMouse)
  151. {
  152. nsCOMPtr<nsIURI> uri;
  153. if (IsLink(getter_AddRefs(uri))) {
  154. if (aTabIndex) {
  155. *aTabIndex = ((sTabFocusModel & eTabFocus_linksMask) == 0 ? -1 : 0);
  156. }
  157. return true;
  158. }
  159. if (nsSVGElement::IsFocusableInternal(aTabIndex, aWithMouse)) {
  160. return true;
  161. }
  162. if (aTabIndex) {
  163. *aTabIndex = -1;
  164. }
  165. return false;
  166. }
  167. bool
  168. SVGAElement::IsLink(nsIURI** aURI) const
  169. {
  170. // To be a clickable XLink for styling and interaction purposes, we require:
  171. //
  172. // xlink:href - must be set
  173. // xlink:type - must be unset or set to "" or set to "simple"
  174. // xlink:show - must be unset or set to "", "new" or "replace"
  175. // xlink:actuate - must be unset or set to "" or "onRequest"
  176. //
  177. // For any other values, we're either not a *clickable* XLink, or the end
  178. // result is poorly specified. Either way, we return false.
  179. static nsIContent::AttrValuesArray sTypeVals[] =
  180. { &nsGkAtoms::_empty, &nsGkAtoms::simple, nullptr };
  181. static nsIContent::AttrValuesArray sShowVals[] =
  182. { &nsGkAtoms::_empty, &nsGkAtoms::_new, &nsGkAtoms::replace, nullptr };
  183. static nsIContent::AttrValuesArray sActuateVals[] =
  184. { &nsGkAtoms::_empty, &nsGkAtoms::onRequest, nullptr };
  185. // Optimization: check for href first for early return
  186. bool useXLink = !HasAttr(kNameSpaceID_None, nsGkAtoms::href);
  187. const nsAttrValue* href =
  188. useXLink
  189. ? mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_XLink)
  190. : mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_None);
  191. if (href &&
  192. FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::type,
  193. sTypeVals, eCaseMatters) !=
  194. nsIContent::ATTR_VALUE_NO_MATCH &&
  195. FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show,
  196. sShowVals, eCaseMatters) !=
  197. nsIContent::ATTR_VALUE_NO_MATCH &&
  198. FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::actuate,
  199. sActuateVals, eCaseMatters) !=
  200. nsIContent::ATTR_VALUE_NO_MATCH) {
  201. nsCOMPtr<nsIURI> baseURI = GetBaseURI();
  202. // Get absolute URI
  203. nsAutoString str;
  204. const uint8_t idx = useXLink ? XLINK_HREF : HREF;
  205. mStringAttributes[idx].GetAnimValue(str, this);
  206. nsContentUtils::NewURIWithDocumentCharset(aURI, str, OwnerDoc(), baseURI);
  207. // must promise out param is non-null if we return true
  208. return !!*aURI;
  209. }
  210. *aURI = nullptr;
  211. return false;
  212. }
  213. void
  214. SVGAElement::GetLinkTarget(nsAString& aTarget)
  215. {
  216. mStringAttributes[TARGET].GetAnimValue(aTarget, this);
  217. if (aTarget.IsEmpty()) {
  218. static nsIContent::AttrValuesArray sShowVals[] =
  219. { &nsGkAtoms::_new, &nsGkAtoms::replace, nullptr };
  220. switch (FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show,
  221. sShowVals, eCaseMatters)) {
  222. case 0:
  223. aTarget.AssignLiteral("_blank");
  224. return;
  225. case 1:
  226. return;
  227. }
  228. nsIDocument* ownerDoc = OwnerDoc();
  229. if (ownerDoc) {
  230. ownerDoc->GetBaseTarget(aTarget);
  231. }
  232. }
  233. }
  234. EventStates
  235. SVGAElement::IntrinsicState() const
  236. {
  237. return Link::LinkState() | SVGAElementBase::IntrinsicState();
  238. }
  239. nsresult
  240. SVGAElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
  241. nsIAtom* aPrefix, const nsAString& aValue,
  242. bool aNotify)
  243. {
  244. nsresult rv = SVGAElementBase::SetAttr(aNameSpaceID, aName, aPrefix,
  245. aValue, aNotify);
  246. // The ordering of the parent class's SetAttr call and Link::ResetLinkState
  247. // is important here! The attribute is not set until SetAttr returns, and
  248. // we will need the updated attribute value because notifying the document
  249. // that content states have changed will call IntrinsicState, which will try
  250. // to get updated information about the visitedness from Link.
  251. if (aName == nsGkAtoms::href &&
  252. (aNameSpaceID == kNameSpaceID_XLink ||
  253. aNameSpaceID == kNameSpaceID_None)) {
  254. Link::ResetLinkState(!!aNotify, true);
  255. }
  256. return rv;
  257. }
  258. nsresult
  259. SVGAElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr,
  260. bool aNotify)
  261. {
  262. nsresult rv = nsSVGElement::UnsetAttr(aNameSpaceID, aAttr, aNotify);
  263. // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
  264. // is important here! The attribute is not unset until UnsetAttr returns, and
  265. // we will need the updated attribute value because notifying the document
  266. // that content states have changed will call IntrinsicState, which will try
  267. // to get updated information about the visitedness from Link.
  268. if (aAttr == nsGkAtoms::href &&
  269. (aNameSpaceID == kNameSpaceID_XLink ||
  270. aNameSpaceID == kNameSpaceID_None)) {
  271. bool hasHref = HasAttr(kNameSpaceID_None, nsGkAtoms::href) ||
  272. HasAttr(kNameSpaceID_XLink, nsGkAtoms::href);
  273. Link::ResetLinkState(!!aNotify, hasHref);
  274. }
  275. return rv;
  276. }
  277. //----------------------------------------------------------------------
  278. // nsSVGElement methods
  279. nsSVGElement::StringAttributesInfo
  280. SVGAElement::GetStringInfo()
  281. {
  282. return StringAttributesInfo(mStringAttributes, sStringInfo,
  283. ArrayLength(sStringInfo));
  284. }
  285. } // namespace dom
  286. } // namespace mozilla