mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-15 08:53:07 +09:00
In order to tailor certain security checks to the caller that is attempting to load a particular piece of content, we need to be able to attach an appropriate triggering principal to the corresponding requests. Since most HTML content is loaded based on attribute values, this means capturing the subject principal of the caller who sets those attributes, which in turn means we need to make it available to `AfterSetAttr` hooks on all relevant element types.
350 lines
10 KiB
C++
350 lines
10 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* 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::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
|
{
|
|
nsresult rv = Element::GetEventTargetParent(aVisitor);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return 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(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,
|
|
nsIPrincipal* aSubjectPrincipal,
|
|
bool aNotify)
|
|
{
|
|
nsresult rv = SVGAElementBase::SetAttr(aNameSpaceID, aName, aPrefix,
|
|
aValue, aSubjectPrincipal,
|
|
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
|