summaryrefslogtreecommitdiffstats
path: root/layout/svg/nsSVGFilterFrame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/svg/nsSVGFilterFrame.cpp')
-rw-r--r--layout/svg/nsSVGFilterFrame.cpp212
1 files changed, 212 insertions, 0 deletions
diff --git a/layout/svg/nsSVGFilterFrame.cpp b/layout/svg/nsSVGFilterFrame.cpp
new file mode 100644
index 000000000..13ce16993
--- /dev/null
+++ b/layout/svg/nsSVGFilterFrame.cpp
@@ -0,0 +1,212 @@
+/* -*- 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 "nsSVGEffects.h"
+#include "nsSVGElement.h"
+#include "mozilla/dom/SVGFilterElement.h"
+#include "nsSVGFilterInstance.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_RAII nsSVGFilterFrame::AutoFilterReferencer
+{
+public:
+ explicit 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
+ MOZ_ASSERT(!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<SVGFilterElement *>(mContent)->mEnumAttributes[aIndex];
+
+ if (thisEnum.IsExplicitlySet())
+ return thisEnum.GetAnimValue();
+
+ AutoFilterReferencer filterRef(this);
+
+ nsSVGFilterFrame *next = GetReferencedFilterIfNotInUse();
+ return next ? next->GetEnumValue(aIndex, aDefault) :
+ static_cast<SVGFilterElement *>(aDefault)->
+ mEnumAttributes[aIndex].GetAnimValue();
+}
+
+const nsSVGLength2 *
+nsSVGFilterFrame::GetLengthValue(uint32_t aIndex, nsIContent *aDefault)
+{
+ const nsSVGLength2 *thisLength =
+ &static_cast<SVGFilterElement *>(mContent)->mLengthAttributes[aIndex];
+
+ if (thisLength->IsExplicitlySet())
+ return thisLength;
+
+ AutoFilterReferencer filterRef(this);
+
+ nsSVGFilterFrame *next = GetReferencedFilterIfNotInUse();
+ return next ? next->GetLengthValue(aIndex, aDefault) :
+ &static_cast<SVGFilterElement *>(aDefault)->mLengthAttributes[aIndex];
+}
+
+const SVGFilterElement *
+nsSVGFilterFrame::GetFilterContent(nsIContent *aDefault)
+{
+ for (nsIContent* child = mContent->GetFirstChild();
+ child;
+ child = child->GetNextSibling()) {
+ RefPtr<nsSVGFE> primitive;
+ CallQueryInterface(child, (nsSVGFE**)getter_AddRefs(primitive));
+ if (primitive) {
+ return static_cast<SVGFilterElement *>(mContent);
+ }
+ }
+
+ AutoFilterReferencer filterRef(this);
+
+ nsSVGFilterFrame *next = GetReferencedFilterIfNotInUse();
+ return next ? next->GetFilterContent(aDefault) :
+ static_cast<SVGFilterElement *>(aDefault);
+}
+
+nsSVGFilterFrame *
+nsSVGFilterFrame::GetReferencedFilter()
+{
+ if (mNoHRefURI)
+ return nullptr;
+
+ nsSVGPaintingProperty *property =
+ Properties().Get(nsSVGEffects::HrefAsPaintingProperty());
+
+ if (!property) {
+ // Fetch our Filter element's href or xlink:href attribute
+ SVGFilterElement *filter = static_cast<SVGFilterElement *>(mContent);
+ nsAutoString href;
+ if (filter->mStringAttributes[SVGFilterElement::HREF].IsExplicitlySet()) {
+ filter->mStringAttributes[SVGFilterElement::HREF]
+ .GetAnimValue(href, filter);
+ } else {
+ filter->mStringAttributes[SVGFilterElement::XLINK_HREF]
+ .GetAnimValue(href, filter);
+ }
+
+ if (href.IsEmpty()) {
+ mNoHRefURI = true;
+ return nullptr; // no URL
+ }
+
+ // Convert href to an nsIURI
+ nsCOMPtr<nsIURI> targetURI;
+ nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
+ nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
+ mContent->GetUncomposedDoc(), base);
+
+ property =
+ nsSVGEffects::GetPaintingProperty(targetURI, this,
+ nsSVGEffects::HrefAsPaintingProperty());
+ 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<nsSVGFilterFrame*>(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 ||
+ aNameSpaceID == kNameSpaceID_None) &&
+ aAttribute == nsGkAtoms::href) {
+ // Blow away our reference, if any
+ Properties().Delete(nsSVGEffects::HrefAsPaintingProperty());
+ mNoHRefURI = false;
+ // And update whoever references us
+ nsSVGEffects::InvalidateDirectRenderingObservers(this);
+ }
+ return nsSVGContainerFrame::AttributeChanged(aNameSpaceID,
+ aAttribute, aModType);
+}
+
+#ifdef DEBUG
+void
+nsSVGFilterFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::filter),
+ "Content is not an SVG filter");
+
+ nsSVGContainerFrame::Init(aContent, aParent, aPrevInFlow);
+}
+#endif /* DEBUG */
+
+nsIAtom *
+nsSVGFilterFrame::GetType() const
+{
+ return nsGkAtoms::svgFilterFrame;
+}