forked from mirrors/gecko-dev
This replaces the hash map of attributes with a tagged union. In this case, all filter attributes will be stored in line, with the exception of some more complex attributes which have an internal nsTArray of floats. This should help avoid all the hashing and extra heap allocations. Depends on D4899 Differential Revision: https://phabricator.services.mozilla.com/D4900 --HG-- extra : moz-landing-system : lando
380 lines
11 KiB
C++
380 lines
11 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/dom/SVGFEImageElement.h"
|
|
|
|
#include "mozilla/EventStateManager.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 SVGFEImageElement_Binding::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,
|
|
imgINotificationObserver, nsIImageLoadingContent)
|
|
|
|
//----------------------------------------------------------------------
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
// Mark channel as urgent-start before load image if the image load is
|
|
// initaiated by a user interaction.
|
|
mUseUrgentStartForChannel = EventStateManager::IsHandlingUserInput();
|
|
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 nsAtom* name) const
|
|
{
|
|
static const MappedAttributeEntry* const map[] = {
|
|
sGraphicsMap
|
|
};
|
|
|
|
return FindAttributeDependence(name, map) ||
|
|
SVGFEImageElementBase::IsAttributeMapped(name);
|
|
}
|
|
|
|
nsresult
|
|
SVGFEImageElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
|
|
const nsAttrValue* aValue,
|
|
const nsAttrValue* aOldValue,
|
|
nsIPrincipal* aSubjectPrincipal,
|
|
bool aNotify)
|
|
{
|
|
if (aName == nsGkAtoms::href &&
|
|
(aNamespaceID == kNameSpaceID_XLink ||
|
|
aNamespaceID == kNameSpaceID_None)) {
|
|
|
|
if (aValue) {
|
|
LoadSVGImage(true, aNotify);
|
|
} else {
|
|
CancelImageRequests(aNotify);
|
|
}
|
|
}
|
|
|
|
return SVGFEImageElementBase::AfterSetAttr(aNamespaceID, aName,
|
|
aValue, aOldValue,
|
|
aSubjectPrincipal,
|
|
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)
|
|
{
|
|
nsresult rv = SVGFEImageElementBase::BindToTree(aDocument, aParent,
|
|
aBindingParent);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent);
|
|
|
|
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("dom::SVGFEImageElement::MaybeLoadSVGImage",
|
|
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();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsINode 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) {
|
|
uint32_t flags = imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY;
|
|
image = imageContainer->GetFrame(imgIContainer::FRAME_CURRENT, flags);
|
|
}
|
|
|
|
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);
|
|
ImageAttributes atts;
|
|
atts.mFilter = (uint32_t)samplingFilter;
|
|
atts.mTransform = TM;
|
|
|
|
// Append the image to aInputImages and store its index in the description.
|
|
size_t imageIndex = aInputImages.Length();
|
|
aInputImages.AppendElement(image);
|
|
atts.mInputIndex = (uint32_t)imageIndex;
|
|
descr.Attributes() = AsVariant(std::move(atts));
|
|
|
|
return descr;
|
|
}
|
|
|
|
bool
|
|
SVGFEImageElement::AttributeAffectsRendering(int32_t aNameSpaceID,
|
|
nsAtom* 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(imgIContainer::FLAG_NONE);
|
|
}
|
|
|
|
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
|