summaryrefslogtreecommitdiffstats
path: root/dom/svg/SVGAElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/svg/SVGAElement.cpp')
-rw-r--r--dom/svg/SVGAElement.cpp349
1 files changed, 349 insertions, 0 deletions
diff --git a/dom/svg/SVGAElement.cpp b/dom/svg/SVGAElement.cpp
new file mode 100644
index 000000000..a69c60ee4
--- /dev/null
+++ b/dom/svg/SVGAElement.cpp
@@ -0,0 +1,349 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/SVGAElement.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/EventStates.h"
+#include "mozilla/dom/SVGAElementBinding.h"
+#include "nsCOMPtr.h"
+#include "nsContentUtils.h"
+#include "nsGkAtoms.h"
+#include "nsSVGString.h"
+#include "nsIURI.h"
+
+NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(A)
+
+namespace mozilla {
+namespace dom {
+
+JSObject*
+SVGAElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return SVGAElementBinding::Wrap(aCx, this, aGivenProto);
+}
+
+nsSVGElement::StringInfo SVGAElement::sStringInfo[3] =
+{
+ { &nsGkAtoms::href, kNameSpaceID_None, true },
+ { &nsGkAtoms::href, kNameSpaceID_XLink, true },
+ { &nsGkAtoms::target, kNameSpaceID_None, true }
+};
+
+
+//----------------------------------------------------------------------
+// nsISupports methods
+
+NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(SVGAElement)
+ NS_INTERFACE_TABLE_INHERITED(SVGAElement,
+ nsIDOMNode,
+ nsIDOMElement,
+ nsIDOMSVGElement,
+ Link)
+NS_INTERFACE_TABLE_TAIL_INHERITING(SVGAElementBase)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(SVGAElement)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGAElement,
+ SVGAElementBase)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGAElement,
+ SVGAElementBase)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_ADDREF_INHERITED(SVGAElement, SVGAElementBase)
+NS_IMPL_RELEASE_INHERITED(SVGAElement, SVGAElementBase)
+
+//----------------------------------------------------------------------
+// Implementation
+
+SVGAElement::SVGAElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+ : SVGAElementBase(aNodeInfo)
+ , Link(this)
+{
+}
+
+SVGAElement::~SVGAElement()
+{
+}
+
+already_AddRefed<SVGAnimatedString>
+SVGAElement::Href()
+{
+ return mStringAttributes[HREF].IsExplicitlySet()
+ ? mStringAttributes[HREF].ToDOMAnimatedString(this)
+ : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this);
+}
+
+//----------------------------------------------------------------------
+// nsINode methods
+
+nsresult
+SVGAElement::PreHandleEvent(EventChainPreVisitor& aVisitor)
+{
+ nsresult rv = Element::PreHandleEvent(aVisitor);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return PreHandleEventForLinks(aVisitor);
+}
+
+nsresult
+SVGAElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
+{
+ return PostHandleEventForLinks(aVisitor);
+}
+
+NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGAElement)
+
+
+//----------------------------------------------------------------------
+
+already_AddRefed<SVGAnimatedString>
+SVGAElement::Target()
+{
+ return mStringAttributes[TARGET].ToDOMAnimatedString(this);
+}
+
+void
+SVGAElement::GetDownload(nsAString & aDownload)
+{
+ GetAttr(kNameSpaceID_None, nsGkAtoms::download, aDownload);
+}
+
+void
+SVGAElement::SetDownload(const nsAString & aDownload, ErrorResult& rv)
+{
+ rv = SetAttr(kNameSpaceID_None, nsGkAtoms::download, aDownload, true);
+}
+
+//----------------------------------------------------------------------
+// nsIContent methods
+
+nsresult
+SVGAElement::BindToTree(nsIDocument *aDocument, nsIContent *aParent,
+ nsIContent *aBindingParent,
+ bool aCompileEventHandlers)
+{
+ Link::ResetLinkState(false, Link::ElementHasHref());
+
+ nsresult rv = SVGAElementBase::BindToTree(aDocument, aParent,
+ aBindingParent,
+ aCompileEventHandlers);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsIDocument* doc = GetComposedDoc();
+ if (doc) {
+ doc->RegisterPendingLinkUpdate(this);
+ }
+
+ return NS_OK;
+}
+
+void
+SVGAElement::UnbindFromTree(bool aDeep, bool aNullParent)
+{
+ // If this link is ever reinserted into a document, it might
+ // be under a different xml:base, so forget the cached state now.
+ Link::ResetLinkState(false, Link::ElementHasHref());
+
+ // Note, we need to use OwnerDoc() here since GetComposedDoc() may
+ // return null already at this point.
+ nsIDocument* doc = OwnerDoc();
+ if (doc) {
+ doc->UnregisterPendingLinkUpdate(this);
+ }
+
+ SVGAElementBase::UnbindFromTree(aDeep, aNullParent);
+}
+
+already_AddRefed<nsIURI>
+SVGAElement::GetHrefURI() const
+{
+ nsCOMPtr<nsIURI> hrefURI;
+ return IsLink(getter_AddRefs(hrefURI)) ? hrefURI.forget() : nullptr;
+}
+
+
+NS_IMETHODIMP_(bool)
+SVGAElement::IsAttributeMapped(const nsIAtom* name) const
+{
+ static const MappedAttributeEntry* const map[] = {
+ sFEFloodMap,
+ sFiltersMap,
+ sFontSpecificationMap,
+ sGradientStopMap,
+ sLightingEffectsMap,
+ sMarkersMap,
+ sTextContentElementsMap,
+ sViewportsMap
+ };
+
+ return FindAttributeDependence(name, map) ||
+ SVGAElementBase::IsAttributeMapped(name);
+}
+
+bool
+SVGAElement::IsFocusableInternal(int32_t *aTabIndex, bool aWithMouse)
+{
+ nsCOMPtr<nsIURI> uri;
+ if (IsLink(getter_AddRefs(uri))) {
+ if (aTabIndex) {
+ *aTabIndex = ((sTabFocusModel & eTabFocus_linksMask) == 0 ? -1 : 0);
+ }
+ return true;
+ }
+ if (nsSVGElement::IsFocusableInternal(aTabIndex, aWithMouse)) {
+ return true;
+ }
+
+ if (aTabIndex) {
+ *aTabIndex = -1;
+ }
+
+ return false;
+}
+
+bool
+SVGAElement::IsLink(nsIURI** aURI) const
+{
+ // To be a clickable XLink for styling and interaction purposes, we require:
+ //
+ // xlink:href - must be set
+ // xlink:type - must be unset or set to "" or set to "simple"
+ // xlink:show - must be unset or set to "", "new" or "replace"
+ // xlink:actuate - must be unset or set to "" or "onRequest"
+ //
+ // For any other values, we're either not a *clickable* XLink, or the end
+ // result is poorly specified. Either way, we return false.
+
+ static nsIContent::AttrValuesArray sTypeVals[] =
+ { &nsGkAtoms::_empty, &nsGkAtoms::simple, nullptr };
+
+ static nsIContent::AttrValuesArray sShowVals[] =
+ { &nsGkAtoms::_empty, &nsGkAtoms::_new, &nsGkAtoms::replace, nullptr };
+
+ static nsIContent::AttrValuesArray sActuateVals[] =
+ { &nsGkAtoms::_empty, &nsGkAtoms::onRequest, nullptr };
+
+ // Optimization: check for href first for early return
+ bool useXLink = !HasAttr(kNameSpaceID_None, nsGkAtoms::href);
+ const nsAttrValue* href =
+ useXLink
+ ? mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_XLink)
+ : mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_None);
+
+ if (href &&
+ FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::type,
+ sTypeVals, eCaseMatters) !=
+ nsIContent::ATTR_VALUE_NO_MATCH &&
+ FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show,
+ sShowVals, eCaseMatters) !=
+ nsIContent::ATTR_VALUE_NO_MATCH &&
+ FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::actuate,
+ sActuateVals, eCaseMatters) !=
+ nsIContent::ATTR_VALUE_NO_MATCH) {
+ nsCOMPtr<nsIURI> baseURI = GetBaseURI();
+ // Get absolute URI
+ nsAutoString str;
+ const uint8_t idx = useXLink ? XLINK_HREF : HREF;
+ mStringAttributes[idx].GetAnimValue(str, this);
+ nsContentUtils::NewURIWithDocumentCharset(aURI, str, OwnerDoc(), baseURI);
+ // must promise out param is non-null if we return true
+ return !!*aURI;
+ }
+
+ *aURI = nullptr;
+ return false;
+}
+
+void
+SVGAElement::GetLinkTarget(nsAString& aTarget)
+{
+ mStringAttributes[TARGET].GetAnimValue(aTarget, this);
+ if (aTarget.IsEmpty()) {
+
+ static nsIContent::AttrValuesArray sShowVals[] =
+ { &nsGkAtoms::_new, &nsGkAtoms::replace, nullptr };
+
+ switch (FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show,
+ sShowVals, eCaseMatters)) {
+ case 0:
+ aTarget.AssignLiteral("_blank");
+ return;
+ case 1:
+ return;
+ }
+ nsIDocument* ownerDoc = OwnerDoc();
+ if (ownerDoc) {
+ ownerDoc->GetBaseTarget(aTarget);
+ }
+ }
+}
+
+EventStates
+SVGAElement::IntrinsicState() const
+{
+ return Link::LinkState() | SVGAElementBase::IntrinsicState();
+}
+
+nsresult
+SVGAElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+ nsIAtom* aPrefix, const nsAString& aValue,
+ bool aNotify)
+{
+ nsresult rv = SVGAElementBase::SetAttr(aNameSpaceID, aName, aPrefix,
+ aValue, aNotify);
+
+ // The ordering of the parent class's SetAttr call and Link::ResetLinkState
+ // is important here! The attribute is not set until SetAttr returns, and
+ // we will need the updated attribute value because notifying the document
+ // that content states have changed will call IntrinsicState, which will try
+ // to get updated information about the visitedness from Link.
+ if (aName == nsGkAtoms::href &&
+ (aNameSpaceID == kNameSpaceID_XLink ||
+ aNameSpaceID == kNameSpaceID_None)) {
+ Link::ResetLinkState(!!aNotify, true);
+ }
+
+ return rv;
+}
+
+nsresult
+SVGAElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr,
+ bool aNotify)
+{
+ nsresult rv = nsSVGElement::UnsetAttr(aNameSpaceID, aAttr, aNotify);
+
+ // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
+ // is important here! The attribute is not unset until UnsetAttr returns, and
+ // we will need the updated attribute value because notifying the document
+ // that content states have changed will call IntrinsicState, which will try
+ // to get updated information about the visitedness from Link.
+ if (aAttr == nsGkAtoms::href &&
+ (aNameSpaceID == kNameSpaceID_XLink ||
+ aNameSpaceID == kNameSpaceID_None)) {
+ bool hasHref = HasAttr(kNameSpaceID_None, nsGkAtoms::href) ||
+ HasAttr(kNameSpaceID_XLink, nsGkAtoms::href);
+ Link::ResetLinkState(!!aNotify, hasHref);
+ }
+
+ return rv;
+}
+
+//----------------------------------------------------------------------
+// nsSVGElement methods
+
+nsSVGElement::StringAttributesInfo
+SVGAElement::GetStringInfo()
+{
+ return StringAttributesInfo(mStringAttributes, sStringInfo,
+ ArrayLength(sStringInfo));
+}
+
+} // namespace dom
+} // namespace mozilla