diff options
Diffstat (limited to 'dom/svg/SVGFEImageElement.cpp')
-rw-r--r-- | dom/svg/SVGFEImageElement.cpp | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/dom/svg/SVGFEImageElement.cpp b/dom/svg/SVGFEImageElement.cpp new file mode 100644 index 000000000..f235d5ccb --- /dev/null +++ b/dom/svg/SVGFEImageElement.cpp @@ -0,0 +1,378 @@ +/* -*- 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/SVGFEImageElement.h" + +#include "mozilla/EventStates.h" +#include "mozilla/dom/SVGFEImageElementBinding.h" +#include "mozilla/dom/SVGFilterElement.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/RefPtr.h" +#include "nsContentUtils.h" +#include "nsLayoutUtils.h" +#include "nsSVGUtils.h" +#include "nsNetUtil.h" +#include "imgIContainer.h" +#include "gfx2DGlue.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEImage) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFEImageElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEImageElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::StringInfo SVGFEImageElement::sStringInfo[3] = +{ + { &nsGkAtoms::result, kNameSpaceID_None, true }, + { &nsGkAtoms::href, kNameSpaceID_None, true }, + { &nsGkAtoms::href, kNameSpaceID_XLink, true } +}; + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_ISUPPORTS_INHERITED(SVGFEImageElement, SVGFEImageElementBase, + nsIDOMNode, nsIDOMElement, nsIDOMSVGElement, + imgINotificationObserver, nsIImageLoadingContent, + imgIOnloadBlocker) + +//---------------------------------------------------------------------- +// Implementation + +SVGFEImageElement::SVGFEImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEImageElementBase(aNodeInfo) +{ + // We start out broken + AddStatesSilently(NS_EVENT_STATE_BROKEN); +} + +SVGFEImageElement::~SVGFEImageElement() +{ + DestroyImageLoadingContent(); +} + +//---------------------------------------------------------------------- + +nsresult +SVGFEImageElement::LoadSVGImage(bool aForce, bool aNotify) +{ + // resolve href attribute + nsCOMPtr<nsIURI> baseURI = GetBaseURI(); + + nsAutoString href; + if (mStringAttributes[HREF].IsExplicitlySet()) { + mStringAttributes[HREF].GetAnimValue(href, this); + } else { + mStringAttributes[XLINK_HREF].GetAnimValue(href, this); + } + href.Trim(" \t\n\r"); + + if (baseURI && !href.IsEmpty()) + NS_MakeAbsoluteURI(href, href, baseURI); + + // Make sure we don't get in a recursive death-spiral + nsIDocument* doc = OwnerDoc(); + nsCOMPtr<nsIURI> hrefAsURI; + if (NS_SUCCEEDED(StringToURI(href, doc, getter_AddRefs(hrefAsURI)))) { + bool isEqual; + if (NS_SUCCEEDED(hrefAsURI->Equals(baseURI, &isEqual)) && isEqual) { + // Image URI matches our URI exactly! Bail out. + return NS_OK; + } + } + + return LoadImage(href, aForce, aNotify, eImageLoadType_Normal); +} + +//---------------------------------------------------------------------- +// EventTarget methods: + +void +SVGFEImageElement::AsyncEventRunning(AsyncEventDispatcher* aEvent) +{ + nsImageLoadingContent::AsyncEventRunning(aEvent); +} + +//---------------------------------------------------------------------- +// nsIContent methods: + +NS_IMETHODIMP_(bool) +SVGFEImageElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sGraphicsMap + }; + + return FindAttributeDependence(name, map) || + SVGFEImageElementBase::IsAttributeMapped(name); +} + +nsresult +SVGFEImageElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) +{ + if (aName == nsGkAtoms::href && + (aNamespaceID == kNameSpaceID_XLink || + aNamespaceID == kNameSpaceID_None)) { + + // If there isn't a frame we still need to load the image in case + // the frame is created later e.g. by attaching to a document. + // If there is a frame then it should deal with loading as the image + // url may be animated. + if (!GetPrimaryFrame()) { + if (aValue) { + LoadSVGImage(true, aNotify); + } else { + CancelImageRequests(aNotify); + } + } + } + + return SVGFEImageElementBase::AfterSetAttr(aNamespaceID, aName, + aValue, aNotify); +} + +void +SVGFEImageElement::MaybeLoadSVGImage() +{ + if ((mStringAttributes[HREF].IsExplicitlySet() || + mStringAttributes[XLINK_HREF].IsExplicitlySet() ) && + (NS_FAILED(LoadSVGImage(false, true)) || + !LoadingEnabled())) { + CancelImageRequests(true); + } +} + +nsresult +SVGFEImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) +{ + nsresult rv = SVGFEImageElementBase::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + + nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent, + aCompileEventHandlers); + + if (mStringAttributes[HREF].IsExplicitlySet() || + mStringAttributes[XLINK_HREF].IsExplicitlySet()) { + // FIXME: Bug 660963 it would be nice if we could just have + // ClearBrokenState update our state and do it fast... + ClearBrokenState(); + RemoveStatesSilently(NS_EVENT_STATE_BROKEN); + nsContentUtils::AddScriptRunner( + NewRunnableMethod(this, &SVGFEImageElement::MaybeLoadSVGImage)); + } + + return rv; +} + +void +SVGFEImageElement::UnbindFromTree(bool aDeep, bool aNullParent) +{ + nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent); + SVGFEImageElementBase::UnbindFromTree(aDeep, aNullParent); +} + +EventStates +SVGFEImageElement::IntrinsicState() const +{ + return SVGFEImageElementBase::IntrinsicState() | + nsImageLoadingContent::ImageState(); +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEImageElement) + +already_AddRefed<SVGAnimatedString> +SVGFEImageElement::Href() +{ + return mStringAttributes[HREF].IsExplicitlySet() + ? mStringAttributes[HREF].ToDOMAnimatedString(this) + : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this); +} + +//---------------------------------------------------------------------- +// nsIDOMSVGFEImageElement methods + +FilterPrimitiveDescription +SVGFEImageElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + nsIFrame* frame = GetPrimaryFrame(); + if (!frame) { + return FilterPrimitiveDescription(PrimitiveType::Empty); + } + + nsCOMPtr<imgIRequest> currentRequest; + GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, + getter_AddRefs(currentRequest)); + + nsCOMPtr<imgIContainer> imageContainer; + if (currentRequest) { + currentRequest->GetImage(getter_AddRefs(imageContainer)); + } + + RefPtr<SourceSurface> image; + if (imageContainer) { + image = imageContainer->GetFrame(imgIContainer::FRAME_CURRENT, + imgIContainer::FLAG_SYNC_DECODE); + } + + if (!image) { + return FilterPrimitiveDescription(PrimitiveType::Empty); + } + + IntSize nativeSize; + imageContainer->GetWidth(&nativeSize.width); + imageContainer->GetHeight(&nativeSize.height); + + Matrix viewBoxTM = + SVGContentUtils::GetViewBoxTransform(aFilterSubregion.width, aFilterSubregion.height, + 0, 0, nativeSize.width, nativeSize.height, + mPreserveAspectRatio); + Matrix TM = viewBoxTM; + TM.PostTranslate(aFilterSubregion.x, aFilterSubregion.y); + + SamplingFilter samplingFilter = nsLayoutUtils::GetSamplingFilterForFrame(frame); + + FilterPrimitiveDescription descr(PrimitiveType::Image); + descr.Attributes().Set(eImageFilter, (uint32_t)samplingFilter); + descr.Attributes().Set(eImageTransform, TM); + + // Append the image to aInputImages and store its index in the description. + size_t imageIndex = aInputImages.Length(); + aInputImages.AppendElement(image); + descr.Attributes().Set(eImageInputIndex, (uint32_t)imageIndex); + + return descr; +} + +bool +SVGFEImageElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + // nsGkAtoms::href is deliberately omitted as the frame has special + // handling to load the image + return SVGFEImageElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) || + (aNameSpaceID == kNameSpaceID_None && + aAttribute == nsGkAtoms::preserveAspectRatio); +} + +bool +SVGFEImageElement::OutputIsTainted(const nsTArray<bool>& aInputsAreTainted, + nsIPrincipal* aReferencePrincipal) +{ + nsresult rv; + nsCOMPtr<imgIRequest> currentRequest; + GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, + getter_AddRefs(currentRequest)); + + if (!currentRequest) { + return false; + } + + uint32_t status; + currentRequest->GetImageStatus(&status); + if ((status & imgIRequest::STATUS_LOAD_COMPLETE) == 0) { + // The load has not completed yet. + return false; + } + + nsCOMPtr<nsIPrincipal> principal; + rv = currentRequest->GetImagePrincipal(getter_AddRefs(principal)); + if (NS_FAILED(rv) || !principal) { + return true; + } + + int32_t corsmode; + if (NS_SUCCEEDED(currentRequest->GetCORSMode(&corsmode)) && + corsmode != imgIRequest::CORS_NONE) { + // If CORS was used to load the image, the page is allowed to read from it. + return false; + } + + if (aReferencePrincipal->Subsumes(principal)) { + // The page is allowed to read from the image. + return false; + } + + return true; +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> +SVGFEImageElement::PreserveAspectRatio() +{ + return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this); +} + +SVGAnimatedPreserveAspectRatio * +SVGFEImageElement::GetPreserveAspectRatio() +{ + return &mPreserveAspectRatio; +} + +nsSVGElement::StringAttributesInfo +SVGFEImageElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +//---------------------------------------------------------------------- +// imgINotificationObserver methods + +NS_IMETHODIMP +SVGFEImageElement::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData) +{ + nsresult rv = nsImageLoadingContent::Notify(aRequest, aType, aData); + + if (aType == imgINotificationObserver::SIZE_AVAILABLE) { + // Request a decode + nsCOMPtr<imgIContainer> container; + aRequest->GetImage(getter_AddRefs(container)); + MOZ_ASSERT(container, "who sent the notification then?"); + container->StartDecoding(); + } + + if (aType == imgINotificationObserver::LOAD_COMPLETE || + aType == imgINotificationObserver::FRAME_UPDATE || + aType == imgINotificationObserver::SIZE_AVAILABLE) { + Invalidate(); + } + + return rv; +} + +//---------------------------------------------------------------------- +// helper methods + +void +SVGFEImageElement::Invalidate() +{ + if (GetParent() && GetParent()->IsSVGElement(nsGkAtoms::filter)) { + static_cast<SVGFilterElement*>(GetParent())->Invalidate(); + } +} + +} // namespace dom +} // namespace mozilla |