diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/svg/SVGAnimationElement.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/svg/SVGAnimationElement.cpp')
-rw-r--r-- | dom/svg/SVGAnimationElement.cpp | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/dom/svg/SVGAnimationElement.cpp b/dom/svg/SVGAnimationElement.cpp new file mode 100644 index 000000000..d6550c96e --- /dev/null +++ b/dom/svg/SVGAnimationElement.cpp @@ -0,0 +1,488 @@ +/* -*- 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/SVGAnimationElement.h" +#include "mozilla/dom/SVGSVGElement.h" +#include "nsSMILTimeContainer.h" +#include "nsSMILAnimationController.h" +#include "nsSMILAnimationFunction.h" +#include "nsContentUtils.h" +#include "nsIContentInlines.h" +#include "nsIURI.h" +#include "prtime.h" + +namespace mozilla { +namespace dom { + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_ADDREF_INHERITED(SVGAnimationElement, SVGAnimationElementBase) +NS_IMPL_RELEASE_INHERITED(SVGAnimationElement, SVGAnimationElementBase) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGAnimationElement) + NS_INTERFACE_MAP_ENTRY(mozilla::dom::SVGTests) +NS_INTERFACE_MAP_END_INHERITING(SVGAnimationElementBase) + +NS_IMPL_CYCLE_COLLECTION_INHERITED(SVGAnimationElement, + SVGAnimationElementBase, + mHrefTarget, mTimedElement) + +//---------------------------------------------------------------------- +// Implementation + +SVGAnimationElement::SVGAnimationElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGAnimationElementBase(aNodeInfo), + mHrefTarget(this) +{ +} + +SVGAnimationElement::~SVGAnimationElement() +{ +} + +nsresult +SVGAnimationElement::Init() +{ + nsresult rv = SVGAnimationElementBase::Init(); + NS_ENSURE_SUCCESS(rv, rv); + + mTimedElement.SetAnimationElement(this); + AnimationFunction().SetAnimationElement(this); + mTimedElement.SetTimeClient(&AnimationFunction()); + + return NS_OK; +} + +//---------------------------------------------------------------------- + +const nsAttrValue* +SVGAnimationElement::GetAnimAttr(nsIAtom* aName) const +{ + return mAttrsAndChildren.GetAttr(aName, kNameSpaceID_None); +} + +bool +SVGAnimationElement::GetAnimAttr(nsIAtom* aAttName, + nsAString& aResult) const +{ + return GetAttr(kNameSpaceID_None, aAttName, aResult); +} + +bool +SVGAnimationElement::HasAnimAttr(nsIAtom* aAttName) const +{ + return HasAttr(kNameSpaceID_None, aAttName); +} + +Element* +SVGAnimationElement::GetTargetElementContent() +{ + if (HasAttr(kNameSpaceID_XLink, nsGkAtoms::href) || + HasAttr(kNameSpaceID_None, nsGkAtoms::href)) { + return mHrefTarget.get(); + } + MOZ_ASSERT(!mHrefTarget.get(), + "We shouldn't have a href target " + "if we don't have an xlink:href or href attribute"); + + // No "href" or "xlink:href" attribute --> I should target my parent. + nsIContent* parent = GetFlattenedTreeParent(); + return parent && parent->IsElement() ? parent->AsElement() : nullptr; +} + +bool +SVGAnimationElement::GetTargetAttributeName(int32_t *aNamespaceID, + nsIAtom **aLocalName) const +{ + const nsAttrValue* nameAttr + = mAttrsAndChildren.GetAttr(nsGkAtoms::attributeName); + + if (!nameAttr) + return false; + + NS_ASSERTION(nameAttr->Type() == nsAttrValue::eAtom, + "attributeName should have been parsed as an atom"); + + return NS_SUCCEEDED(nsContentUtils::SplitQName( + this, nsDependentAtomString(nameAttr->GetAtomValue()), + aNamespaceID, aLocalName)); +} + +nsSMILTargetAttrType +SVGAnimationElement::GetTargetAttributeType() const +{ + nsIContent::AttrValuesArray typeValues[] = { &nsGkAtoms::css, + &nsGkAtoms::XML, + nullptr}; + nsSMILTargetAttrType smilTypes[] = { eSMILTargetAttrType_CSS, + eSMILTargetAttrType_XML }; + int32_t index = FindAttrValueIn(kNameSpaceID_None, + nsGkAtoms::attributeType, + typeValues, + eCaseMatters); + return (index >= 0) ? smilTypes[index] : eSMILTargetAttrType_auto; +} + +nsSMILTimedElement& +SVGAnimationElement::TimedElement() +{ + return mTimedElement; +} + +nsSVGElement* +SVGAnimationElement::GetTargetElement() +{ + FlushAnimations(); + + // We'll just call the other GetTargetElement method, and QI to the right type + nsIContent* target = GetTargetElementContent(); + + return (target && target->IsSVGElement()) + ? static_cast<nsSVGElement*>(target) : nullptr; +} + +float +SVGAnimationElement::GetStartTime(ErrorResult& rv) +{ + FlushAnimations(); + + nsSMILTimeValue startTime = mTimedElement.GetStartTime(); + if (!startTime.IsDefinite()) { + rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return 0.f; + } + + return float(double(startTime.GetMillis()) / PR_MSEC_PER_SEC); +} + +float +SVGAnimationElement::GetCurrentTime() +{ + // Not necessary to call FlushAnimations() for this + + nsSMILTimeContainer* root = GetTimeContainer(); + if (root) { + return float(double(root->GetCurrentTime()) / PR_MSEC_PER_SEC); + } + + return 0.0f; +} + +float +SVGAnimationElement::GetSimpleDuration(ErrorResult& rv) +{ + // Not necessary to call FlushAnimations() for this + + nsSMILTimeValue simpleDur = mTimedElement.GetSimpleDuration(); + if (!simpleDur.IsDefinite()) { + rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return 0.f; + } + + return float(double(simpleDur.GetMillis()) / PR_MSEC_PER_SEC); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +nsresult +SVGAnimationElement::BindToTree(nsIDocument* aDocument, + nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) +{ + MOZ_ASSERT(!mHrefTarget.get(), + "Shouldn't have href-target yet (or it should've been cleared)"); + nsresult rv = SVGAnimationElementBase::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv,rv); + + // XXXdholbert is GetCtx (as a check for SVG parent) still needed here? + if (!GetCtx()) { + // No use proceeding. We don't have an SVG parent (yet) so we won't be able + // to register ourselves etc. Maybe next time we'll have more luck. + // (This sort of situation will arise a lot when trees are being constructed + // piece by piece via script) + return NS_OK; + } + + // Add myself to the animation controller's master set of animation elements. + if (aDocument) { + nsSMILAnimationController *controller = aDocument->GetAnimationController(); + if (controller) { + controller->RegisterAnimationElement(this); + } + const nsAttrValue* href = + HasAttr(kNameSpaceID_None, nsGkAtoms::href) + ? mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_None) + : mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_XLink); + if (href) { + nsAutoString hrefStr; + href->ToString(hrefStr); + + // Pass in |aParent| instead of |this| -- first argument is only used + // for a call to GetComposedDoc(), and |this| might not have a current + // document yet. + UpdateHrefTarget(aParent, hrefStr); + } + + mTimedElement.BindToTree(aParent); + } + + AnimationNeedsResample(); + + return NS_OK; +} + +void +SVGAnimationElement::UnbindFromTree(bool aDeep, bool aNullParent) +{ + nsSMILAnimationController *controller = OwnerDoc()->GetAnimationController(); + if (controller) { + controller->UnregisterAnimationElement(this); + } + + mHrefTarget.Unlink(); + mTimedElement.DissolveReferences(); + + AnimationNeedsResample(); + + SVGAnimationElementBase::UnbindFromTree(aDeep, aNullParent); +} + +bool +SVGAnimationElement::ParseAttribute(int32_t aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) +{ + if (aNamespaceID == kNameSpaceID_None) { + // Deal with target-related attributes here + if (aAttribute == nsGkAtoms::attributeName || + aAttribute == nsGkAtoms::attributeType) { + aResult.ParseAtom(aValue); + AnimationNeedsResample(); + return true; + } + + nsresult rv = NS_ERROR_FAILURE; + + // First let the animation function try to parse it... + bool foundMatch = + AnimationFunction().SetAttr(aAttribute, aValue, aResult, &rv); + + // ... and if that didn't recognize the attribute, let the timed element + // try to parse it. + if (!foundMatch) { + foundMatch = + mTimedElement.SetAttr(aAttribute, aValue, aResult, this, &rv); + } + + if (foundMatch) { + AnimationNeedsResample(); + if (NS_FAILED(rv)) { + ReportAttributeParseFailure(OwnerDoc(), aAttribute, aValue); + return false; + } + return true; + } + } + + return SVGAnimationElementBase::ParseAttribute(aNamespaceID, aAttribute, + aValue, aResult); +} + +nsresult +SVGAnimationElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) +{ + nsresult rv = + SVGAnimationElementBase::AfterSetAttr(aNamespaceID, aName, aValue, + aNotify); + + if (SVGTests::IsConditionalProcessingAttribute(aName)) { + bool isDisabled = !SVGTests::PassesConditionalProcessingTests(); + if (mTimedElement.SetIsDisabled(isDisabled)) { + AnimationNeedsResample(); + } + } + + if (!((aNamespaceID == kNameSpaceID_None || + aNamespaceID == kNameSpaceID_XLink) && + aName == nsGkAtoms::href)) { + return rv; + } + + if (!aValue) { + if (aNamespaceID == kNameSpaceID_None) { + mHrefTarget.Unlink(); + AnimationTargetChanged(); + + // After unsetting href, we may still have xlink:href, so we + // should try to add it back. + const nsAttrValue* xlinkHref = + mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_XLink); + if (xlinkHref) { + UpdateHrefTarget(this, xlinkHref->GetStringValue()); + } + } else if (!HasAttr(kNameSpaceID_None, nsGkAtoms::href)) { + mHrefTarget.Unlink(); + AnimationTargetChanged(); + } // else: we unset xlink:href, but we still have href attribute, so keep + // mHrefTarget linking to href. + } else if (IsInUncomposedDoc() && + !(aNamespaceID == kNameSpaceID_XLink && + HasAttr(kNameSpaceID_None, nsGkAtoms::href))) { + // Note: "href" takes priority over xlink:href. So if "xlink:href" is being + // set here, we only let that update our target if "href" is *unset*. + MOZ_ASSERT(aValue->Type() == nsAttrValue::eString, + "Expected href attribute to be string type"); + UpdateHrefTarget(this, aValue->GetStringValue()); + } // else: we're not yet in a document -- we'll update the target on + // next BindToTree call. + + return rv; +} + +nsresult +SVGAnimationElement::UnsetAttr(int32_t aNamespaceID, + nsIAtom* aAttribute, bool aNotify) +{ + nsresult rv = SVGAnimationElementBase::UnsetAttr(aNamespaceID, aAttribute, + aNotify); + NS_ENSURE_SUCCESS(rv,rv); + + if (aNamespaceID == kNameSpaceID_None) { + if (AnimationFunction().UnsetAttr(aAttribute) || + mTimedElement.UnsetAttr(aAttribute)) { + AnimationNeedsResample(); + } + } + + return NS_OK; +} + +bool +SVGAnimationElement::IsNodeOfType(uint32_t aFlags) const +{ + return !(aFlags & ~(eCONTENT | eANIMATION)); +} + +//---------------------------------------------------------------------- +// SVGTests methods + +bool +SVGAnimationElement::IsInChromeDoc() const +{ + return nsContentUtils::IsChromeDoc(OwnerDoc()); +} + +//---------------------------------------------------------------------- +// SVG utility methods + +void +SVGAnimationElement::ActivateByHyperlink() +{ + FlushAnimations(); + + // The behavior for when the target is an animation element is defined in + // SMIL Animation: + // http://www.w3.org/TR/smil-animation/#HyperlinkSemantics + nsSMILTimeValue seekTime = mTimedElement.GetHyperlinkTime(); + if (seekTime.IsDefinite()) { + nsSMILTimeContainer* timeContainer = GetTimeContainer(); + if (timeContainer) { + timeContainer->SetCurrentTime(seekTime.GetMillis()); + AnimationNeedsResample(); + // As with SVGSVGElement::SetCurrentTime, we need to trigger + // a synchronous sample now. + FlushAnimations(); + } + // else, silently fail. We mustn't be part of an SVG document fragment that + // is attached to the document tree so there's nothing we can do here + } else { + IgnoredErrorResult rv; + BeginElement(rv); + } +} + +//---------------------------------------------------------------------- +// Implementation helpers + +nsSMILTimeContainer* +SVGAnimationElement::GetTimeContainer() +{ + SVGSVGElement *element = SVGContentUtils::GetOuterSVGElement(this); + + if (element) { + return element->GetTimedDocumentRoot(); + } + + return nullptr; +} + +void +SVGAnimationElement::BeginElementAt(float offset, ErrorResult& rv) +{ + // Make sure the timegraph is up-to-date + FlushAnimations(); + + // This will fail if we're not attached to a time container (SVG document + // fragment). + rv = mTimedElement.BeginElementAt(offset); + if (rv.Failed()) + return; + + AnimationNeedsResample(); + // Force synchronous sample so that events resulting from this call arrive in + // the expected order and we get an up-to-date paint. + FlushAnimations(); +} + +void +SVGAnimationElement::EndElementAt(float offset, ErrorResult& rv) +{ + // Make sure the timegraph is up-to-date + FlushAnimations(); + + rv = mTimedElement.EndElementAt(offset); + if (rv.Failed()) + return; + + AnimationNeedsResample(); + // Force synchronous sample + FlushAnimations(); +} + +bool +SVGAnimationElement::IsEventAttributeName(nsIAtom* aName) +{ + return nsContentUtils::IsEventAttributeName(aName, EventNameType_SMIL); +} + +void +SVGAnimationElement::UpdateHrefTarget(nsIContent* aNodeForContext, + const nsAString& aHrefStr) +{ + nsCOMPtr<nsIURI> targetURI; + nsCOMPtr<nsIURI> baseURI = GetBaseURI(); + nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), + aHrefStr, OwnerDoc(), baseURI); + mHrefTarget.Reset(aNodeForContext, targetURI); + AnimationTargetChanged(); +} + +void +SVGAnimationElement::AnimationTargetChanged() +{ + mTimedElement.HandleTargetElementChange(GetTargetElementContent()); + AnimationNeedsResample(); +} + +} // namespace dom +} // namespace mozilla |