diff -r 000000000000 -r 6474c204b198 layout/svg/nsSVGFilterFrame.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layout/svg/nsSVGFilterFrame.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,205 @@ +/* -*- Mode: C++; tab-width: 2; 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/. */ + +// Main header first: +#include "nsSVGFilterFrame.h" + +// Keep others in (case-insensitive) order: +#include "gfxUtils.h" +#include "nsGkAtoms.h" +#include "nsRenderingContext.h" +#include "nsSVGEffects.h" +#include "nsSVGElement.h" +#include "mozilla/dom/SVGFilterElement.h" +#include "nsSVGFilterInstance.h" +#include "nsSVGFilterPaintCallback.h" +#include "nsSVGIntegrationUtils.h" +#include "nsSVGUtils.h" +#include "nsContentUtils.h" + +using namespace mozilla::dom; + +nsIFrame* +NS_NewSVGFilterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) +{ + return new (aPresShell) nsSVGFilterFrame(aContext); +} + +NS_IMPL_FRAMEARENA_HELPERS(nsSVGFilterFrame) + +class MOZ_STACK_CLASS nsSVGFilterFrame::AutoFilterReferencer +{ +public: + AutoFilterReferencer(nsSVGFilterFrame *aFrame MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mFrame(aFrame) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + // Reference loops should normally be detected in advance and handled, so + // we're not expecting to encounter them here + NS_ABORT_IF_FALSE(!mFrame->mLoopFlag, "Undetected reference loop!"); + mFrame->mLoopFlag = true; + } + ~AutoFilterReferencer() { + mFrame->mLoopFlag = false; + } +private: + nsSVGFilterFrame *mFrame; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +uint16_t +nsSVGFilterFrame::GetEnumValue(uint32_t aIndex, nsIContent *aDefault) +{ + nsSVGEnum& thisEnum = + static_cast(mContent)->mEnumAttributes[aIndex]; + + if (thisEnum.IsExplicitlySet()) + return thisEnum.GetAnimValue(); + + AutoFilterReferencer filterRef(this); + + nsSVGFilterFrame *next = GetReferencedFilterIfNotInUse(); + return next ? next->GetEnumValue(aIndex, aDefault) : + static_cast(aDefault)-> + mEnumAttributes[aIndex].GetAnimValue(); +} + +const nsSVGLength2 * +nsSVGFilterFrame::GetLengthValue(uint32_t aIndex, nsIContent *aDefault) +{ + const nsSVGLength2 *thisLength = + &static_cast(mContent)->mLengthAttributes[aIndex]; + + if (thisLength->IsExplicitlySet()) + return thisLength; + + AutoFilterReferencer filterRef(this); + + nsSVGFilterFrame *next = GetReferencedFilterIfNotInUse(); + return next ? next->GetLengthValue(aIndex, aDefault) : + &static_cast(aDefault)->mLengthAttributes[aIndex]; +} + +const SVGFilterElement * +nsSVGFilterFrame::GetFilterContent(nsIContent *aDefault) +{ + for (nsIContent* child = mContent->GetFirstChild(); + child; + child = child->GetNextSibling()) { + nsRefPtr primitive; + CallQueryInterface(child, (nsSVGFE**)getter_AddRefs(primitive)); + if (primitive) { + return static_cast(mContent); + } + } + + AutoFilterReferencer filterRef(this); + + nsSVGFilterFrame *next = GetReferencedFilterIfNotInUse(); + return next ? next->GetFilterContent(aDefault) : + static_cast(aDefault); +} + +nsSVGFilterFrame * +nsSVGFilterFrame::GetReferencedFilter() +{ + if (mNoHRefURI) + return nullptr; + + nsSVGPaintingProperty *property = static_cast + (Properties().Get(nsSVGEffects::HrefProperty())); + + if (!property) { + // Fetch our Filter element's xlink:href attribute + SVGFilterElement *filter = static_cast(mContent); + nsAutoString href; + filter->mStringAttributes[SVGFilterElement::HREF].GetAnimValue(href, filter); + if (href.IsEmpty()) { + mNoHRefURI = true; + return nullptr; // no URL + } + + // Convert href to an nsIURI + nsCOMPtr targetURI; + nsCOMPtr base = mContent->GetBaseURI(); + nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href, + mContent->GetCurrentDoc(), base); + + property = + nsSVGEffects::GetPaintingProperty(targetURI, this, nsSVGEffects::HrefProperty()); + if (!property) + return nullptr; + } + + nsIFrame *result = property->GetReferencedFrame(); + if (!result) + return nullptr; + + nsIAtom* frameType = result->GetType(); + if (frameType != nsGkAtoms::svgFilterFrame) + return nullptr; + + return static_cast(result); +} + +nsSVGFilterFrame * +nsSVGFilterFrame::GetReferencedFilterIfNotInUse() +{ + nsSVGFilterFrame *referenced = GetReferencedFilter(); + if (!referenced) + return nullptr; + + if (referenced->mLoopFlag) { + // XXXjwatt: we should really send an error to the JavaScript Console here: + NS_WARNING("Filter reference loop detected while inheriting attribute!"); + return nullptr; + } + + return referenced; +} + +nsresult +nsSVGFilterFrame::AttributeChanged(int32_t aNameSpaceID, + nsIAtom* aAttribute, + int32_t aModType) +{ + if (aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::x || + aAttribute == nsGkAtoms::y || + aAttribute == nsGkAtoms::width || + aAttribute == nsGkAtoms::height || + aAttribute == nsGkAtoms::filterUnits || + aAttribute == nsGkAtoms::primitiveUnits)) { + nsSVGEffects::InvalidateDirectRenderingObservers(this); + } else if (aNameSpaceID == kNameSpaceID_XLink && + aAttribute == nsGkAtoms::href) { + // Blow away our reference, if any + Properties().Delete(nsSVGEffects::HrefProperty()); + mNoHRefURI = false; + // And update whoever references us + nsSVGEffects::InvalidateDirectRenderingObservers(this); + } + return nsSVGFilterFrameBase::AttributeChanged(aNameSpaceID, + aAttribute, aModType); +} + +#ifdef DEBUG +void +nsSVGFilterFrame::Init(nsIContent* aContent, + nsIFrame* aParent, + nsIFrame* aPrevInFlow) +{ + NS_ASSERTION(aContent->IsSVG(nsGkAtoms::filter), + "Content is not an SVG filter"); + + nsSVGFilterFrameBase::Init(aContent, aParent, aPrevInFlow); +} +#endif /* DEBUG */ + +nsIAtom * +nsSVGFilterFrame::GetType() const +{ + return nsGkAtoms::svgFilterFrame; +}