michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // Main header first: michael@0: #include "nsSVGFilterFrame.h" michael@0: michael@0: // Keep others in (case-insensitive) order: michael@0: #include "gfxUtils.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsSVGEffects.h" michael@0: #include "nsSVGElement.h" michael@0: #include "mozilla/dom/SVGFilterElement.h" michael@0: #include "nsSVGFilterInstance.h" michael@0: #include "nsSVGFilterPaintCallback.h" michael@0: #include "nsSVGIntegrationUtils.h" michael@0: #include "nsSVGUtils.h" michael@0: #include "nsContentUtils.h" michael@0: michael@0: using namespace mozilla::dom; michael@0: michael@0: nsIFrame* michael@0: NS_NewSVGFilterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsSVGFilterFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsSVGFilterFrame) michael@0: michael@0: class MOZ_STACK_CLASS nsSVGFilterFrame::AutoFilterReferencer michael@0: { michael@0: public: michael@0: AutoFilterReferencer(nsSVGFilterFrame *aFrame MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : mFrame(aFrame) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: // Reference loops should normally be detected in advance and handled, so michael@0: // we're not expecting to encounter them here michael@0: NS_ABORT_IF_FALSE(!mFrame->mLoopFlag, "Undetected reference loop!"); michael@0: mFrame->mLoopFlag = true; michael@0: } michael@0: ~AutoFilterReferencer() { michael@0: mFrame->mLoopFlag = false; michael@0: } michael@0: private: michael@0: nsSVGFilterFrame *mFrame; michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: uint16_t michael@0: nsSVGFilterFrame::GetEnumValue(uint32_t aIndex, nsIContent *aDefault) michael@0: { michael@0: nsSVGEnum& thisEnum = michael@0: static_cast(mContent)->mEnumAttributes[aIndex]; michael@0: michael@0: if (thisEnum.IsExplicitlySet()) michael@0: return thisEnum.GetAnimValue(); michael@0: michael@0: AutoFilterReferencer filterRef(this); michael@0: michael@0: nsSVGFilterFrame *next = GetReferencedFilterIfNotInUse(); michael@0: return next ? next->GetEnumValue(aIndex, aDefault) : michael@0: static_cast(aDefault)-> michael@0: mEnumAttributes[aIndex].GetAnimValue(); michael@0: } michael@0: michael@0: const nsSVGLength2 * michael@0: nsSVGFilterFrame::GetLengthValue(uint32_t aIndex, nsIContent *aDefault) michael@0: { michael@0: const nsSVGLength2 *thisLength = michael@0: &static_cast(mContent)->mLengthAttributes[aIndex]; michael@0: michael@0: if (thisLength->IsExplicitlySet()) michael@0: return thisLength; michael@0: michael@0: AutoFilterReferencer filterRef(this); michael@0: michael@0: nsSVGFilterFrame *next = GetReferencedFilterIfNotInUse(); michael@0: return next ? next->GetLengthValue(aIndex, aDefault) : michael@0: &static_cast(aDefault)->mLengthAttributes[aIndex]; michael@0: } michael@0: michael@0: const SVGFilterElement * michael@0: nsSVGFilterFrame::GetFilterContent(nsIContent *aDefault) michael@0: { michael@0: for (nsIContent* child = mContent->GetFirstChild(); michael@0: child; michael@0: child = child->GetNextSibling()) { michael@0: nsRefPtr primitive; michael@0: CallQueryInterface(child, (nsSVGFE**)getter_AddRefs(primitive)); michael@0: if (primitive) { michael@0: return static_cast(mContent); michael@0: } michael@0: } michael@0: michael@0: AutoFilterReferencer filterRef(this); michael@0: michael@0: nsSVGFilterFrame *next = GetReferencedFilterIfNotInUse(); michael@0: return next ? next->GetFilterContent(aDefault) : michael@0: static_cast(aDefault); michael@0: } michael@0: michael@0: nsSVGFilterFrame * michael@0: nsSVGFilterFrame::GetReferencedFilter() michael@0: { michael@0: if (mNoHRefURI) michael@0: return nullptr; michael@0: michael@0: nsSVGPaintingProperty *property = static_cast michael@0: (Properties().Get(nsSVGEffects::HrefProperty())); michael@0: michael@0: if (!property) { michael@0: // Fetch our Filter element's xlink:href attribute michael@0: SVGFilterElement *filter = static_cast(mContent); michael@0: nsAutoString href; michael@0: filter->mStringAttributes[SVGFilterElement::HREF].GetAnimValue(href, filter); michael@0: if (href.IsEmpty()) { michael@0: mNoHRefURI = true; michael@0: return nullptr; // no URL michael@0: } michael@0: michael@0: // Convert href to an nsIURI michael@0: nsCOMPtr targetURI; michael@0: nsCOMPtr base = mContent->GetBaseURI(); michael@0: nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href, michael@0: mContent->GetCurrentDoc(), base); michael@0: michael@0: property = michael@0: nsSVGEffects::GetPaintingProperty(targetURI, this, nsSVGEffects::HrefProperty()); michael@0: if (!property) michael@0: return nullptr; michael@0: } michael@0: michael@0: nsIFrame *result = property->GetReferencedFrame(); michael@0: if (!result) michael@0: return nullptr; michael@0: michael@0: nsIAtom* frameType = result->GetType(); michael@0: if (frameType != nsGkAtoms::svgFilterFrame) michael@0: return nullptr; michael@0: michael@0: return static_cast(result); michael@0: } michael@0: michael@0: nsSVGFilterFrame * michael@0: nsSVGFilterFrame::GetReferencedFilterIfNotInUse() michael@0: { michael@0: nsSVGFilterFrame *referenced = GetReferencedFilter(); michael@0: if (!referenced) michael@0: return nullptr; michael@0: michael@0: if (referenced->mLoopFlag) { michael@0: // XXXjwatt: we should really send an error to the JavaScript Console here: michael@0: NS_WARNING("Filter reference loop detected while inheriting attribute!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: return referenced; michael@0: } michael@0: michael@0: nsresult michael@0: nsSVGFilterFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: if (aNameSpaceID == kNameSpaceID_None && michael@0: (aAttribute == nsGkAtoms::x || michael@0: aAttribute == nsGkAtoms::y || michael@0: aAttribute == nsGkAtoms::width || michael@0: aAttribute == nsGkAtoms::height || michael@0: aAttribute == nsGkAtoms::filterUnits || michael@0: aAttribute == nsGkAtoms::primitiveUnits)) { michael@0: nsSVGEffects::InvalidateDirectRenderingObservers(this); michael@0: } else if (aNameSpaceID == kNameSpaceID_XLink && michael@0: aAttribute == nsGkAtoms::href) { michael@0: // Blow away our reference, if any michael@0: Properties().Delete(nsSVGEffects::HrefProperty()); michael@0: mNoHRefURI = false; michael@0: // And update whoever references us michael@0: nsSVGEffects::InvalidateDirectRenderingObservers(this); michael@0: } michael@0: return nsSVGFilterFrameBase::AttributeChanged(aNameSpaceID, michael@0: aAttribute, aModType); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: nsSVGFilterFrame::Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) michael@0: { michael@0: NS_ASSERTION(aContent->IsSVG(nsGkAtoms::filter), michael@0: "Content is not an SVG filter"); michael@0: michael@0: nsSVGFilterFrameBase::Init(aContent, aParent, aPrevInFlow); michael@0: } michael@0: #endif /* DEBUG */ michael@0: michael@0: nsIAtom * michael@0: nsSVGFilterFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::svgFilterFrame; michael@0: }