mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-26 19:55:39 +00:00
4275cd1039
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, that means capturing the subject principal of the caller who sets those attributes, which means making it available to AfterSetAttr hooks. MozReview-Commit-ID: BMDL2Uepg0X --HG-- extra : rebase_source : 25e438c243700a9368c393e40e3a6002d968d6c8
390 lines
11 KiB
C++
390 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 "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_MAP_BEGIN(SVGAElement)
|
|
NS_INTERFACE_MAP_ENTRY(nsIDOMNode)
|
|
NS_INTERFACE_MAP_ENTRY(nsIDOMElement)
|
|
NS_INTERFACE_MAP_ENTRY(nsIDOMSVGElement)
|
|
NS_INTERFACE_MAP_ENTRY(Link)
|
|
NS_INTERFACE_MAP_END_INHERITING(SVGAElementBase)
|
|
|
|
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);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Link methods
|
|
|
|
bool
|
|
SVGAElement::ElementHasHref() const
|
|
{
|
|
return mStringAttributes[HREF].IsExplicitlySet() ||
|
|
mStringAttributes[XLINK_HREF].IsExplicitlySet();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// 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());
|
|
|
|
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) {
|
|
nsIPresShell* presShell = doc->GetShell();
|
|
if (presShell) {
|
|
nsPresContext* presContext = presShell->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 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 useBareHref = mStringAttributes[HREF].IsExplicitlySet();
|
|
|
|
if ((useBareHref || mStringAttributes[XLINK_HREF].IsExplicitlySet()) &&
|
|
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 = 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 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, nsAtom* aName,
|
|
nsAtom* 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, nsAtom* 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)) {
|
|
Link::ResetLinkState(!!aNotify, Link::ElementHasHref());
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsSVGElement methods
|
|
|
|
nsSVGElement::StringAttributesInfo
|
|
SVGAElement::GetStringInfo()
|
|
{
|
|
return StringAttributesInfo(mStringAttributes, sStringInfo,
|
|
ArrayLength(sStringInfo));
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|