Files
palemoon27/dom/svg/SVGUseElement.cpp
T
roytam1 8fd0c73b26 import changes from `dev' branch of rmottola/Arctic-Fox:
- test part of Bug 789788 - Revise the don't-use-document-fonts option (1b6a0e6ed)
- Bug 1172782 - Change how nsTHashtable::Clear() works. r=froydnj. (c8c2adc51)
- Bug 1180072 - Remove PL_DHashTableEnumerate(). r=froydnj. (b5448efda)
- Bug 1180122 - Make Chaos Mode affect PLDHashTable's iterators. r=froydnj. (11fd0e222)
- Bug 1182516 - Add Chaos Mode environment variable MOZ_CHAOSMODE. r=roc (d636f6c90)
- Bug 1182516 - Fix mid-air conflict with 3fd2ab6cb762 on a CLOSED TREE. r=bustage (8dec46b7a)
- Bug 1184280 - Remove warning about mDisabledJSAndPlugins being false. r=ehsan (c23e96fc1)
- Bug 1181411 - Test some expected aborts in PLDHashTable. r=glandium. (682788df7)
- Bug 1185399 (part 1) - Remove macros from pldhash.h. r=froydnj. (15001ff2a)
- Bug 1185399 (part 2) - Remove macros from pldhash.cpp. r=froydnj. (afeddcc90)
- Bug 1187197 (part 1) - Move comments from PL_DHashTable* functions to the equivalent methods. r=froydnj. (78921590e)
- Bug 1187197 (part 2) - Put function return types on their own line. r=froydnj. (9aae02f83)
- Bug 1187197 (part 3) - Convert pldhash.{cpp,h} to C++ style comments. r=froydnj. (57afe7e1f)
- Bug 1202526 (part 1) - Add PLDHashTable::RemoveEntry(). r=froydnj. (1b5453695)
- Bug 1202526 (part 2) - Avoid PL_DHashTableRawRemove() in nsLoadGroup. r=mcmanus. (1ad20b17f)
- Bug 1202526 (part 3) - Avoid PL_DHashTableRawRemove() in nsDocument. r=bz. (cdde6eff0)
- Bug 1202526 (part 4) - Avoid PL_DHashTableRawRemove() in nsPropertyTable. r=bz. (ed437f4d6)
- Bug 1202526 (part 5) - Use PLDHashTable::RemoveEntry() in nsSecureBrowserUIImpl. r=dkeeler. (ac1d44066)
- Bug 1202526 (part 6) - Use PLDHashTable::RemoveEntry() in XULDocument. r=bz. (2639a053e)
- Bug 1202526 (part 7) - Use PLDHashTable::RemoveEntry() in prefs code. r=bz. (2eb3b1097)
- Bug 1202526 (part 8) - Use PLDHashTable::RemoveEntry() in the cycle collector. r=mccr8. (ec77d7635)
- Bug 1130096 - Convert embedding/components/commandhandler/ to Gecko style. r=mccr8 (a5aecf9de)
- Bug 1184842. Remove layout.frames.force_resizability pref. r=mats (9226b0caf)
- Bug 1184842. Remove frameset mVisibilityOverride since it's always false now. r=mats (1b439feb8)
- Bug 1184842. Change nsAttrAndChildArray::SetAndTakeAttr to nsAttrAndChildArray::SetAndSwapAttr. r=peterv (1442f0eae)
- Bug 1184842. Make SetAttrAndNotify use the real old value instead of aOldValue when possible. r=bz (bc45970f5)
- Bug 1184842. Add aOldValue parameter to nsNodeUtils::AttributeChanged. r=peterv (31bf8c08e)
- Bug 1184842. Pass aOldValue to all mutation observers. r=peterv (e69daf7f7)
- Bug 1184842. Allow BeforeSetAttr to preparse aValue. r=peterv (9c51655de)
- follow up fix to Bug 1184842. Allow BeforeSetAttr to preparse aValue (fb997a066)
- Bug 1149042 - Call AttributeWillChange before a style="" attribute gets created when touching element.style. r=smaug (41f2ea361)
- Bug 1184842. Pass preparsed attribute values to nsNodeUtils::AttributeWillChange. r=peterv (646622252)
- Bug 1013743, MutationObserver should observe only the subtree it is attached to, r=wchen (ad606eb15)
- Bug 1184842. Add aNewValue to nsIMutationObserver::AttributeWillChange. r=peterv (796042972)
- Bug 1184842. Preparse class attribute values in Element::BeforeSetAttr. r=peterv (e18989b2b)
- Bug 1154149 - Remove nsPresContext arguments from a bunch of nsStyleSBug 1154149 - Remove nsPresContext arguments from a bunch of nsStyleS (b7797ef66)
- Bug 1184842. Route aOldValue/aNewValue to AttributeData. r=heycam (14489941b)
- Bug 1184842. Restyling should consider only the classes that have changed. r=heycam (5a5e670dc)
- Bug 1121760 (part 1) - Remove PL_DHashTableSearch(). r=poiru. (d92813655)
2021-03-13 11:00:00 +08:00

490 lines
15 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/ArrayUtils.h"
#include "mozilla/dom/SVGUseElement.h"
#include "mozilla/dom/SVGUseElementBinding.h"
#include "nsGkAtoms.h"
#include "mozilla/dom/SVGSVGElement.h"
#include "nsIDocument.h"
#include "nsIPresShell.h"
#include "mozilla/dom/Element.h"
#include "nsContentUtils.h"
#include "nsIURI.h"
NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Use)
namespace mozilla {
namespace dom {
JSObject*
SVGUseElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
{
return SVGUseElementBinding::Wrap(aCx, this, aGivenProto);
}
////////////////////////////////////////////////////////////////////////
// implementation
nsSVGElement::LengthInfo SVGUseElement::sLengthInfo[4] =
{
{ &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X },
{ &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y },
{ &nsGkAtoms::width, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X },
{ &nsGkAtoms::height, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y },
};
nsSVGElement::StringInfo SVGUseElement::sStringInfo[1] =
{
{ &nsGkAtoms::href, kNameSpaceID_XLink, true }
};
//----------------------------------------------------------------------
// nsISupports methods
NS_IMPL_CYCLE_COLLECTION_CLASS(SVGUseElement)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGUseElement,
SVGUseElementBase)
nsAutoScriptBlocker scriptBlocker;
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginal)
tmp->DestroyAnonymousContent();
tmp->UnlinkSource();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGUseElement,
SVGUseElementBase)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginal)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClone)
tmp->mSource.Traverse(&cb);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_ADDREF_INHERITED(SVGUseElement,SVGUseElementBase)
NS_IMPL_RELEASE_INHERITED(SVGUseElement,SVGUseElementBase)
NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(SVGUseElement)
NS_INTERFACE_TABLE_INHERITED(SVGUseElement, nsIMutationObserver)
NS_INTERFACE_TABLE_TAIL_INHERITING(SVGUseElementBase)
//----------------------------------------------------------------------
// Implementation
SVGUseElement::SVGUseElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
: SVGUseElementBase(aNodeInfo), mSource(this)
{
}
SVGUseElement::~SVGUseElement()
{
UnlinkSource();
}
//----------------------------------------------------------------------
// nsIDOMNode methods
nsresult
SVGUseElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const
{
*aResult = nullptr;
already_AddRefed<mozilla::dom::NodeInfo> ni = nsRefPtr<mozilla::dom::NodeInfo>(aNodeInfo).forget();
SVGUseElement *it = new SVGUseElement(ni);
nsCOMPtr<nsINode> kungFuDeathGrip(it);
nsresult rv1 = it->Init();
nsresult rv2 = const_cast<SVGUseElement*>(this)->CopyInnerTo(it);
// SVGUseElement specific portion - record who we cloned from
it->mOriginal = const_cast<SVGUseElement*>(this);
if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
kungFuDeathGrip.swap(*aResult);
}
return NS_FAILED(rv1) ? rv1 : rv2;
}
already_AddRefed<SVGAnimatedString>
SVGUseElement::Href()
{
return mStringAttributes[HREF].ToDOMAnimatedString(this);
}
//----------------------------------------------------------------------
already_AddRefed<SVGAnimatedLength>
SVGUseElement::X()
{
return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this);
}
already_AddRefed<SVGAnimatedLength>
SVGUseElement::Y()
{
return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this);
}
already_AddRefed<SVGAnimatedLength>
SVGUseElement::Width()
{
return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);
}
already_AddRefed<SVGAnimatedLength>
SVGUseElement::Height()
{
return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
}
//----------------------------------------------------------------------
// nsIMutationObserver methods
void
SVGUseElement::CharacterDataChanged(nsIDocument *aDocument,
nsIContent *aContent,
CharacterDataChangeInfo* aInfo)
{
if (nsContentUtils::IsInSameAnonymousTree(this, aContent)) {
TriggerReclone();
}
}
void
SVGUseElement::AttributeChanged(nsIDocument* aDocument,
Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType,
const nsAttrValue* aOldValue)
{
if (nsContentUtils::IsInSameAnonymousTree(this, aElement)) {
TriggerReclone();
}
}
void
SVGUseElement::ContentAppended(nsIDocument *aDocument,
nsIContent *aContainer,
nsIContent *aFirstNewContent,
int32_t aNewIndexInContainer)
{
if (nsContentUtils::IsInSameAnonymousTree(this, aContainer)) {
TriggerReclone();
}
}
void
SVGUseElement::ContentInserted(nsIDocument *aDocument,
nsIContent *aContainer,
nsIContent *aChild,
int32_t aIndexInContainer)
{
if (nsContentUtils::IsInSameAnonymousTree(this, aChild)) {
TriggerReclone();
}
}
void
SVGUseElement::ContentRemoved(nsIDocument *aDocument,
nsIContent *aContainer,
nsIContent *aChild,
int32_t aIndexInContainer,
nsIContent *aPreviousSibling)
{
if (nsContentUtils::IsInSameAnonymousTree(this, aChild)) {
TriggerReclone();
}
}
void
SVGUseElement::NodeWillBeDestroyed(const nsINode *aNode)
{
nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
UnlinkSource();
}
//----------------------------------------------------------------------
nsIContent*
SVGUseElement::CreateAnonymousContent()
{
mClone = nullptr;
if (mSource.get()) {
mSource.get()->RemoveMutationObserver(this);
}
LookupHref();
nsIContent* targetContent = mSource.get();
if (!targetContent)
return nullptr;
// make sure target is valid type for <use>
// QIable nsSVGGraphicsElement would eliminate enumerating all elements
if (!targetContent->IsAnyOfSVGElements(nsGkAtoms::svg,
nsGkAtoms::symbol,
nsGkAtoms::g,
nsGkAtoms::path,
nsGkAtoms::text,
nsGkAtoms::rect,
nsGkAtoms::circle,
nsGkAtoms::ellipse,
nsGkAtoms::line,
nsGkAtoms::polyline,
nsGkAtoms::polygon,
nsGkAtoms::image,
nsGkAtoms::use))
return nullptr;
// circular loop detection
// check 1 - check if we're a document descendent of the target
if (nsContentUtils::ContentIsDescendantOf(this, targetContent))
return nullptr;
// check 2 - check if we're a clone, and if we already exist in the hierarchy
if (GetParent() && mOriginal) {
for (nsCOMPtr<nsIContent> content = GetParent();
content;
content = content->GetParent()) {
if (content->IsSVGElement(nsGkAtoms::use) &&
static_cast<SVGUseElement*>(content.get())->mOriginal == mOriginal) {
return nullptr;
}
}
}
nsCOMPtr<nsINode> newnode;
nsCOMArray<nsINode> unused;
nsNodeInfoManager* nodeInfoManager =
targetContent->OwnerDoc() == OwnerDoc() ?
nullptr : OwnerDoc()->NodeInfoManager();
nsNodeUtils::Clone(targetContent, true, nodeInfoManager, unused,
getter_AddRefs(newnode));
nsCOMPtr<nsIContent> newcontent = do_QueryInterface(newnode);
if (!newcontent)
return nullptr;
if (newcontent->IsSVGElement(nsGkAtoms::symbol)) {
nsIDocument *document = GetComposedDoc();
if (!document)
return nullptr;
nsNodeInfoManager *nodeInfoManager = document->NodeInfoManager();
if (!nodeInfoManager)
return nullptr;
nsRefPtr<mozilla::dom::NodeInfo> nodeInfo;
nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::svg, nullptr,
kNameSpaceID_SVG,
nsIDOMNode::ELEMENT_NODE);
nsCOMPtr<nsIContent> svgNode;
NS_NewSVGSVGElement(getter_AddRefs(svgNode), nodeInfo.forget(),
NOT_FROM_PARSER);
if (!svgNode)
return nullptr;
// copy attributes
const nsAttrName* name;
uint32_t i;
for (i = 0; (name = newcontent->GetAttrNameAt(i)); i++) {
nsAutoString value;
int32_t nsID = name->NamespaceID();
nsIAtom* lname = name->LocalName();
newcontent->GetAttr(nsID, lname, value);
svgNode->SetAttr(nsID, lname, name->GetPrefix(), value, false);
}
// move the children over
uint32_t num = newcontent->GetChildCount();
for (i = 0; i < num; i++) {
nsCOMPtr<nsIContent> child = newcontent->GetFirstChild();
newcontent->RemoveChildAt(0, false);
svgNode->InsertChildAt(child, i, true);
}
newcontent = svgNode;
}
if (newcontent->IsAnyOfSVGElements(nsGkAtoms::svg, nsGkAtoms::symbol)) {
nsSVGElement *newElement = static_cast<nsSVGElement*>(newcontent.get());
if (mLengthAttributes[ATTR_WIDTH].IsExplicitlySet())
newElement->SetLength(nsGkAtoms::width, mLengthAttributes[ATTR_WIDTH]);
if (mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet())
newElement->SetLength(nsGkAtoms::height, mLengthAttributes[ATTR_HEIGHT]);
}
// Set up its base URI correctly
nsCOMPtr<nsIURI> baseURI = targetContent->GetBaseURI();
if (!baseURI)
return nullptr;
newcontent->SetExplicitBaseURI(baseURI);
targetContent->AddMutationObserver(this);
mClone = newcontent;
return mClone;
}
void
SVGUseElement::DestroyAnonymousContent()
{
nsContentUtils::DestroyAnonymousContent(&mClone);
}
bool
SVGUseElement::OurWidthAndHeightAreUsed() const
{
return mClone && mClone->IsAnyOfSVGElements(nsGkAtoms::svg, nsGkAtoms::symbol);
}
//----------------------------------------------------------------------
// implementation helpers
void
SVGUseElement::SyncWidthOrHeight(nsIAtom* aName)
{
NS_ASSERTION(aName == nsGkAtoms::width || aName == nsGkAtoms::height,
"The clue is in the function name");
NS_ASSERTION(OurWidthAndHeightAreUsed(), "Don't call this");
if (OurWidthAndHeightAreUsed()) {
nsSVGElement *target = static_cast<nsSVGElement*>(mClone.get());
uint32_t index = *sLengthInfo[ATTR_WIDTH].mName == aName ? ATTR_WIDTH : ATTR_HEIGHT;
if (mLengthAttributes[index].IsExplicitlySet()) {
target->SetLength(aName, mLengthAttributes[index]);
return;
}
if (mClone->IsSVGElement(nsGkAtoms::svg)) {
// Our width/height attribute is now no longer explicitly set, so we
// need to revert the clone's width/height to the width/height of the
// content that's being cloned.
TriggerReclone();
return;
}
// Our width/height attribute is now no longer explicitly set, so we
// need to set the value to 100%
nsSVGLength2 length;
length.Init(SVGContentUtils::XY, 0xff,
100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE);
target->SetLength(aName, length);
return;
}
}
void
SVGUseElement::LookupHref()
{
nsAutoString href;
mStringAttributes[HREF].GetAnimValue(href, this);
if (href.IsEmpty())
return;
nsCOMPtr<nsIURI> targetURI;
nsCOMPtr<nsIURI> baseURI = mOriginal ? mOriginal->GetBaseURI() : GetBaseURI();
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
GetComposedDoc(), baseURI);
mSource.Reset(this, targetURI);
}
void
SVGUseElement::TriggerReclone()
{
nsIDocument *doc = GetComposedDoc();
if (!doc)
return;
nsIPresShell *presShell = doc->GetShell();
if (!presShell)
return;
presShell->PostRecreateFramesFor(this);
}
void
SVGUseElement::UnlinkSource()
{
if (mSource.get()) {
mSource.get()->RemoveMutationObserver(this);
}
mSource.Unlink();
}
//----------------------------------------------------------------------
// nsSVGElement methods
/* virtual */ gfxMatrix
SVGUseElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix,
TransformTypes aWhich) const
{
// 'transform' attribute:
gfxMatrix fromUserSpace =
SVGUseElementBase::PrependLocalTransformsTo(aMatrix, aWhich);
if (aWhich == eUserSpaceToParent) {
return fromUserSpace;
}
// our 'x' and 'y' attributes:
float x, y;
const_cast<SVGUseElement*>(this)->GetAnimatedLengthValues(&x, &y, nullptr);
gfxMatrix toUserSpace = gfxMatrix::Translation(x, y);
if (aWhich == eChildToUserSpace) {
return toUserSpace * aMatrix;
}
MOZ_ASSERT(aWhich == eAllTransforms, "Unknown TransformTypes");
return toUserSpace * fromUserSpace;
}
/* virtual */ bool
SVGUseElement::HasValidDimensions() const
{
return (!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() ||
mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) &&
(!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() ||
mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0);
}
nsSVGElement::LengthAttributesInfo
SVGUseElement::GetLengthInfo()
{
return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
ArrayLength(sLengthInfo));
}
nsSVGElement::StringAttributesInfo
SVGUseElement::GetStringInfo()
{
return StringAttributesInfo(mStringAttributes, sStringInfo,
ArrayLength(sStringInfo));
}
//----------------------------------------------------------------------
// nsIContent methods
NS_IMETHODIMP_(bool)
SVGUseElement::IsAttributeMapped(const nsIAtom* name) const
{
static const MappedAttributeEntry* const map[] = {
sFEFloodMap,
sFiltersMap,
sFontSpecificationMap,
sGradientStopMap,
sLightingEffectsMap,
sMarkersMap,
sTextContentElementsMap,
sViewportsMap
};
return FindAttributeDependence(name, map) ||
SVGUseElementBase::IsAttributeMapped(name);
}
} // namespace dom
} // namespace mozilla