mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 16:55:40 +00:00
846d8789ee
Various places in dom/ use the pattern: already_AddRefed<NodeInfo> ni = ...; which is supposed to be disallowed by our static analysis code, but isn't, for whatever reason. To fix our static analysis code, we need to eliminate instances of the above pattern. Unfortunately, eliminating this pattern requires restructuring how Nodes are created. Most Node subclasses take `already_AddRefed<NodeInfo>&` in their constructors, and a few accept `already_AddRefed<NodeInfo>&&`. We need to enforce the latter pattern consistently, which requires changing dozens of source files.
449 lines
11 KiB
C++
449 lines
11 KiB
C++
/* -*- 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 "nsIContentInlines.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 SVGAElement_Binding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
nsSVGElement::StringInfo SVGAElement::sStringInfo[3] =
|
|
{
|
|
{ &nsGkAtoms::href, kNameSpaceID_None, true },
|
|
{ &nsGkAtoms::href, kNameSpaceID_XLink, true },
|
|
{ &nsGkAtoms::target, kNameSpaceID_None, true }
|
|
};
|
|
|
|
// static
|
|
const DOMTokenListSupportedToken SVGAElement::sSupportedRelValues[] = {
|
|
"noreferrer",
|
|
"noopener",
|
|
nullptr
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsISupports methods
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGAElement)
|
|
NS_INTERFACE_MAP_ENTRY(Link)
|
|
NS_INTERFACE_MAP_END_INHERITING(SVGAElementBase)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(SVGAElement,
|
|
SVGAElementBase,
|
|
mRelList)
|
|
|
|
NS_IMPL_ADDREF_INHERITED(SVGAElement, SVGAElementBase)
|
|
NS_IMPL_RELEASE_INHERITED(SVGAElement, SVGAElementBase)
|
|
|
|
//----------------------------------------------------------------------
|
|
// Implementation
|
|
|
|
SVGAElement::SVGAElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
|
|
: SVGAElementBase(std::move(aNodeInfo))
|
|
, Link(this)
|
|
{
|
|
}
|
|
|
|
SVGAElement::~SVGAElement()
|
|
{
|
|
}
|
|
|
|
already_AddRefed<SVGAnimatedString>
|
|
SVGAElement::Href()
|
|
{
|
|
return mStringAttributes[HREF].IsExplicitlySet()
|
|
? mStringAttributes[HREF].ToDOMAnimatedString(this)
|
|
: mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Link methods
|
|
|
|
bool
|
|
SVGAElement::ElementHasHref() const
|
|
{
|
|
return mStringAttributes[HREF].IsExplicitlySet() ||
|
|
mStringAttributes[XLINK_HREF].IsExplicitlySet();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsINode methods
|
|
|
|
void
|
|
SVGAElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
|
{
|
|
Element::GetEventTargetParent(aVisitor);
|
|
|
|
GetEventTargetParentForLinks(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(nsGkAtoms::download, aDownload);
|
|
}
|
|
|
|
void
|
|
SVGAElement::SetDownload(const nsAString& aDownload, ErrorResult& rv)
|
|
{
|
|
SetAttr(nsGkAtoms::download, aDownload, rv);
|
|
}
|
|
|
|
void
|
|
SVGAElement::GetPing(nsAString& aPing)
|
|
{
|
|
GetAttr(nsGkAtoms::ping, aPing);
|
|
}
|
|
|
|
void
|
|
SVGAElement::SetPing(const nsAString& aPing, ErrorResult& rv)
|
|
{
|
|
SetAttr(nsGkAtoms::ping, aPing, rv);
|
|
}
|
|
|
|
void
|
|
SVGAElement::GetRel(nsAString& aRel)
|
|
{
|
|
GetAttr(nsGkAtoms::rel, aRel);
|
|
}
|
|
|
|
void
|
|
SVGAElement::SetRel(const nsAString& aRel, ErrorResult& rv)
|
|
{
|
|
SetAttr(nsGkAtoms::rel, aRel, rv);
|
|
}
|
|
|
|
void
|
|
SVGAElement::GetReferrerPolicy(nsAString& aPolicy)
|
|
{
|
|
GetEnumAttr(nsGkAtoms::referrerpolicy, EmptyCString().get(), aPolicy);
|
|
}
|
|
|
|
void
|
|
SVGAElement::SetReferrerPolicy(const nsAString& aPolicy,
|
|
mozilla::ErrorResult& rv)
|
|
{
|
|
SetAttr(nsGkAtoms::referrerpolicy, aPolicy, rv);
|
|
}
|
|
|
|
nsDOMTokenList*
|
|
SVGAElement:: RelList()
|
|
{
|
|
if (!mRelList) {
|
|
mRelList = new nsDOMTokenList(this, nsGkAtoms::rel, sSupportedRelValues);
|
|
}
|
|
return mRelList;
|
|
}
|
|
|
|
void
|
|
SVGAElement::GetHreflang(nsAString& aHreflang)
|
|
{
|
|
GetAttr(nsGkAtoms::hreflang, aHreflang);
|
|
}
|
|
|
|
void
|
|
SVGAElement::SetHreflang(const nsAString& aHreflang, mozilla::ErrorResult& rv)
|
|
{
|
|
SetAttr(nsGkAtoms::hreflang, aHreflang, rv);
|
|
}
|
|
|
|
void SVGAElement::GetType(nsAString& aType)
|
|
{
|
|
GetAttr(nsGkAtoms::type, aType);
|
|
}
|
|
|
|
void SVGAElement::SetType(const nsAString& aType, mozilla::ErrorResult& rv)
|
|
{
|
|
SetAttr(nsGkAtoms::type, aType, rv);
|
|
}
|
|
|
|
void SVGAElement::GetText(nsAString& aText, mozilla::ErrorResult& rv)
|
|
{
|
|
if (NS_WARN_IF(!nsContentUtils::GetNodeTextContent(this, true, aText, fallible))) {
|
|
rv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
|
}
|
|
}
|
|
|
|
void SVGAElement::SetText(const nsAString& aText, mozilla::ErrorResult& rv)
|
|
{
|
|
rv = nsContentUtils::SetNodeTextContent(this, aText, false);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsIContent methods
|
|
|
|
nsresult
|
|
SVGAElement::BindToTree(nsIDocument *aDocument, nsIContent *aParent,
|
|
nsIContent *aBindingParent)
|
|
{
|
|
Link::ResetLinkState(false, Link::ElementHasHref());
|
|
|
|
nsresult rv = SVGAElementBase::BindToTree(aDocument, aParent,
|
|
aBindingParent);
|
|
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());
|
|
|
|
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 nsAtom* name) const
|
|
{
|
|
static const MappedAttributeEntry* const map[] = {
|
|
sFEFloodMap,
|
|
sFiltersMap,
|
|
sFontSpecificationMap,
|
|
sGradientStopMap,
|
|
sLightingEffectsMap,
|
|
sMarkersMap,
|
|
sTextContentElementsMap,
|
|
sViewportsMap
|
|
};
|
|
|
|
return FindAttributeDependence(name, map) ||
|
|
SVGAElementBase::IsAttributeMapped(name);
|
|
}
|
|
|
|
int32_t
|
|
SVGAElement::TabIndexDefault()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static bool
|
|
IsNodeInEditableRegion(nsINode* aNode)
|
|
{
|
|
while (aNode) {
|
|
if (aNode->IsEditable()) {
|
|
return true;
|
|
}
|
|
aNode = aNode->GetParent();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
SVGAElement::IsSVGFocusable(bool* aIsFocusable, int32_t* aTabIndex)
|
|
{
|
|
if (nsSVGElement::IsSVGFocusable(aIsFocusable, aTabIndex)) {
|
|
return true;
|
|
}
|
|
|
|
// cannot focus links if there is no link handler
|
|
nsIDocument* doc = GetComposedDoc();
|
|
if (doc) {
|
|
nsPresContext* presContext = doc->GetPresContext();
|
|
if (presContext && !presContext->GetLinkHandler()) {
|
|
*aIsFocusable = false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Links that are in an editable region should never be focusable, even if
|
|
// they are in a contenteditable="false" region.
|
|
if (IsNodeInEditableRegion(this)) {
|
|
if (aTabIndex) {
|
|
*aTabIndex = -1;
|
|
}
|
|
|
|
*aIsFocusable = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
if (!HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
|
|
// check whether we're actually a link
|
|
if (!Link::HasURI()) {
|
|
// Not tabbable or focusable without href (bug 17605), unless
|
|
// forced to be via presence of nonnegative tabindex attribute
|
|
if (aTabIndex) {
|
|
*aTabIndex = -1;
|
|
}
|
|
|
|
*aIsFocusable = false;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (aTabIndex && (sTabFocusModel & eTabFocus_linksMask) == 0) {
|
|
*aTabIndex = -1;
|
|
}
|
|
|
|
*aIsFocusable = true;
|
|
|
|
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 Element::AttrValuesArray sTypeVals[] =
|
|
{ &nsGkAtoms::_empty, &nsGkAtoms::simple, nullptr };
|
|
|
|
static Element::AttrValuesArray sShowVals[] =
|
|
{ &nsGkAtoms::_empty, &nsGkAtoms::_new, &nsGkAtoms::replace, nullptr };
|
|
|
|
static Element::AttrValuesArray sActuateVals[] =
|
|
{ &nsGkAtoms::_empty, &nsGkAtoms::onRequest, nullptr };
|
|
|
|
// Optimization: check for href first for early return
|
|
bool useBareHref = mStringAttributes[HREF].IsExplicitlySet();
|
|
|
|
if ((useBareHref || mStringAttributes[XLINK_HREF].IsExplicitlySet()) &&
|
|
FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::type,
|
|
sTypeVals, eCaseMatters) !=
|
|
Element::ATTR_VALUE_NO_MATCH &&
|
|
FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show,
|
|
sShowVals, eCaseMatters) !=
|
|
Element::ATTR_VALUE_NO_MATCH &&
|
|
FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::actuate,
|
|
sActuateVals, eCaseMatters) !=
|
|
Element::ATTR_VALUE_NO_MATCH) {
|
|
nsCOMPtr<nsIURI> baseURI = GetBaseURI();
|
|
// Get absolute URI
|
|
nsAutoString str;
|
|
const uint8_t idx = useBareHref ? HREF : XLINK_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 Element::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::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
|
|
const nsAttrValue* aValue,
|
|
const nsAttrValue* aOldValue,
|
|
nsIPrincipal* aMaybeScriptedPrincipal,
|
|
bool aNotify)
|
|
{
|
|
if (aName == nsGkAtoms::href &&
|
|
(aNameSpaceID == kNameSpaceID_XLink ||
|
|
aNameSpaceID == kNameSpaceID_None)) {
|
|
// We can't assume that null aValue means we no longer have an href, because
|
|
// we could be unsetting xlink:href but still have a null-namespace href, or
|
|
// vice versa. But we can fast-path the case when we _do_ have a new value.
|
|
Link::ResetLinkState(aNotify, aValue || Link::ElementHasHref());
|
|
}
|
|
|
|
return SVGAElementBase::AfterSetAttr(aNameSpaceID, aName, aValue, aOldValue,
|
|
aMaybeScriptedPrincipal, aNotify);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsSVGElement methods
|
|
|
|
nsSVGElement::StringAttributesInfo
|
|
SVGAElement::GetStringInfo()
|
|
{
|
|
return StringAttributesInfo(mStringAttributes, sStringInfo,
|
|
ArrayLength(sStringInfo));
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|