mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 00:05:36 +00:00
e74f1cd513
In order to facilitate the movement of code with side-effects called by Element::SetAttr to Element::BeforeSetAttr and Element::AfterSetAttr, Element::AfterSetAttr should have access to the old value of the attribute. This includes information about whether there was previously a value set or not. Accomplishing this involved passing an additional argument through functions that find and change the old attribute value in order to ensure that we can differentiate between an empty old value and an absent old value (attribute was not set). Note that while I tried to ensure that accurate values (and their absence) are reported to Element::AfterSetAttr, I largely ignored SVG. While the old value reported for SVG values should be however accurate the value already being reported to SetAttrAndNotify was, SVG elements do not currently report unset values properly because they will never pass a null pointer to SetAttrAndNotify. MozReview-Commit-ID: K1mha8CNFZP --HG-- extra : rebase_source : 42776eb01451d371e4aebcc17fe3dd112c8d268b
328 lines
9.4 KiB
C++
328 lines
9.4 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/ArrayUtils.h"
|
|
#include "mozilla/EventStateManager.h"
|
|
#include "mozilla/EventStates.h"
|
|
|
|
#include "mozilla/dom/SVGImageElement.h"
|
|
#include "mozilla/gfx/2D.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsIURI.h"
|
|
#include "nsNetUtil.h"
|
|
#include "imgINotificationObserver.h"
|
|
#include "mozilla/dom/SVGImageElementBinding.h"
|
|
#include "nsContentUtils.h"
|
|
|
|
NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Image)
|
|
|
|
using namespace mozilla::gfx;
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
JSObject*
|
|
SVGImageElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
|
|
{
|
|
return SVGImageElementBinding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
nsSVGElement::LengthInfo SVGImageElement::sLengthInfo[4] =
|
|
{
|
|
{ &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X },
|
|
{ &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y },
|
|
{ &nsGkAtoms::width, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X },
|
|
{ &nsGkAtoms::height, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y },
|
|
};
|
|
|
|
nsSVGElement::StringInfo SVGImageElement::sStringInfo[2] =
|
|
{
|
|
{ &nsGkAtoms::href, kNameSpaceID_None, true },
|
|
{ &nsGkAtoms::href, kNameSpaceID_XLink, true }
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsISupports methods
|
|
|
|
NS_IMPL_ISUPPORTS_INHERITED(SVGImageElement, SVGImageElementBase,
|
|
nsIDOMNode, nsIDOMElement,
|
|
nsIDOMSVGElement,
|
|
imgINotificationObserver,
|
|
nsIImageLoadingContent, imgIOnloadBlocker)
|
|
|
|
//----------------------------------------------------------------------
|
|
// Implementation
|
|
|
|
SVGImageElement::SVGImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
|
|
: SVGImageElementBase(aNodeInfo)
|
|
{
|
|
// We start out broken
|
|
AddStatesSilently(NS_EVENT_STATE_BROKEN);
|
|
}
|
|
|
|
SVGImageElement::~SVGImageElement()
|
|
{
|
|
DestroyImageLoadingContent();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsIDOMNode methods
|
|
|
|
|
|
NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGImageElement)
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
already_AddRefed<SVGAnimatedLength>
|
|
SVGImageElement::X()
|
|
{
|
|
return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this);
|
|
}
|
|
|
|
already_AddRefed<SVGAnimatedLength>
|
|
SVGImageElement::Y()
|
|
{
|
|
return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this);
|
|
}
|
|
|
|
already_AddRefed<SVGAnimatedLength>
|
|
SVGImageElement::Width()
|
|
{
|
|
return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);
|
|
}
|
|
|
|
already_AddRefed<SVGAnimatedLength>
|
|
SVGImageElement::Height()
|
|
{
|
|
return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
|
|
}
|
|
|
|
already_AddRefed<DOMSVGAnimatedPreserveAspectRatio>
|
|
SVGImageElement::PreserveAspectRatio()
|
|
{
|
|
return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this);
|
|
}
|
|
|
|
already_AddRefed<SVGAnimatedString>
|
|
SVGImageElement::Href()
|
|
{
|
|
return mStringAttributes[HREF].IsExplicitlySet()
|
|
? mStringAttributes[HREF].ToDOMAnimatedString(this)
|
|
: mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
nsresult
|
|
SVGImageElement::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);
|
|
|
|
// 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
|
|
SVGImageElement::AsyncEventRunning(AsyncEventDispatcher* aEvent)
|
|
{
|
|
nsImageLoadingContent::AsyncEventRunning(aEvent);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsIContent methods:
|
|
|
|
nsresult
|
|
SVGImageElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
|
|
const nsAttrValue* aValue,
|
|
const nsAttrValue* aOldValue, bool aNotify)
|
|
{
|
|
if (aName == nsGkAtoms::href &&
|
|
(aNamespaceID == kNameSpaceID_None ||
|
|
aNamespaceID == kNameSpaceID_XLink)) {
|
|
|
|
if (aValue) {
|
|
LoadSVGImage(true, aNotify);
|
|
} else {
|
|
CancelImageRequests(aNotify);
|
|
}
|
|
}
|
|
return SVGImageElementBase::AfterSetAttr(aNamespaceID, aName,
|
|
aValue, aOldValue, aNotify);
|
|
}
|
|
|
|
void
|
|
SVGImageElement::MaybeLoadSVGImage()
|
|
{
|
|
if ((mStringAttributes[HREF].IsExplicitlySet() ||
|
|
mStringAttributes[XLINK_HREF].IsExplicitlySet()) &&
|
|
(NS_FAILED(LoadSVGImage(false, true)) ||
|
|
!LoadingEnabled())) {
|
|
CancelImageRequests(true);
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
SVGImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|
nsIContent* aBindingParent,
|
|
bool aCompileEventHandlers)
|
|
{
|
|
nsresult rv = SVGImageElementBase::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, &SVGImageElement::MaybeLoadSVGImage));
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
SVGImageElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
|
{
|
|
nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
|
|
SVGImageElementBase::UnbindFromTree(aDeep, aNullParent);
|
|
}
|
|
|
|
EventStates
|
|
SVGImageElement::IntrinsicState() const
|
|
{
|
|
return SVGImageElementBase::IntrinsicState() |
|
|
nsImageLoadingContent::ImageState();
|
|
}
|
|
|
|
NS_IMETHODIMP_(bool)
|
|
SVGImageElement::IsAttributeMapped(const nsIAtom* name) const
|
|
{
|
|
static const MappedAttributeEntry* const map[] = {
|
|
sViewportsMap,
|
|
};
|
|
|
|
return FindAttributeDependence(name, map) ||
|
|
SVGImageElementBase::IsAttributeMapped(name);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// SVGGeometryElement methods
|
|
|
|
/* For the purposes of the update/invalidation logic pretend to
|
|
be a rectangle. */
|
|
bool
|
|
SVGImageElement::GetGeometryBounds(Rect* aBounds,
|
|
const StrokeOptions& aStrokeOptions,
|
|
const Matrix& aToBoundsSpace,
|
|
const Matrix* aToNonScalingStrokeSpace)
|
|
{
|
|
Rect rect;
|
|
GetAnimatedLengthValues(&rect.x, &rect.y, &rect.width,
|
|
&rect.height, nullptr);
|
|
|
|
if (rect.IsEmpty()) {
|
|
// Rendering of the element disabled
|
|
rect.SetEmpty(); // Make sure width/height are zero and not negative
|
|
}
|
|
|
|
*aBounds = aToBoundsSpace.TransformBounds(rect);
|
|
return true;
|
|
}
|
|
|
|
already_AddRefed<Path>
|
|
SVGImageElement::BuildPath(PathBuilder* aBuilder)
|
|
{
|
|
// We get called in order to get bounds for this element, and for
|
|
// hit-testing against it. For that we just pretend to be a rectangle.
|
|
|
|
float x, y, width, height;
|
|
GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
|
|
|
|
if (width <= 0 || height <= 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
Rect r(x, y, width, height);
|
|
aBuilder->MoveTo(r.TopLeft());
|
|
aBuilder->LineTo(r.TopRight());
|
|
aBuilder->LineTo(r.BottomRight());
|
|
aBuilder->LineTo(r.BottomLeft());
|
|
aBuilder->Close();
|
|
|
|
return aBuilder->Finish();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsSVGElement methods
|
|
|
|
/* virtual */ bool
|
|
SVGImageElement::HasValidDimensions() const
|
|
{
|
|
return mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() &&
|
|
mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0 &&
|
|
mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() &&
|
|
mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0;
|
|
}
|
|
|
|
nsSVGElement::LengthAttributesInfo
|
|
SVGImageElement::GetLengthInfo()
|
|
{
|
|
return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
|
|
ArrayLength(sLengthInfo));
|
|
}
|
|
|
|
SVGAnimatedPreserveAspectRatio *
|
|
SVGImageElement::GetPreserveAspectRatio()
|
|
{
|
|
return &mPreserveAspectRatio;
|
|
}
|
|
|
|
nsSVGElement::StringAttributesInfo
|
|
SVGImageElement::GetStringInfo()
|
|
{
|
|
return StringAttributesInfo(mStringAttributes, sStringInfo,
|
|
ArrayLength(sStringInfo));
|
|
}
|
|
|
|
nsresult
|
|
SVGImageElement::CopyInnerTo(Element* aDest, bool aPreallocateChildren)
|
|
{
|
|
if (aDest->OwnerDoc()->IsStaticDocument()) {
|
|
CreateStaticImageClone(static_cast<SVGImageElement*>(aDest));
|
|
}
|
|
return SVGImageElementBase::CopyInnerTo(aDest, aPreallocateChildren);
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|
|
|