2015-05-03 22:32:37 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2013-01-03 10:17:02 +04:00
|
|
|
/* 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/. */
|
|
|
|
|
2018-12-27 20:30:38 +03:00
|
|
|
#include "SVGTransformableElement.h"
|
|
|
|
|
|
|
|
#include "DOMSVGAnimatedTransformList.h"
|
2014-03-18 06:41:33 +04:00
|
|
|
#include "gfx2DGlue.h"
|
2018-02-09 19:17:10 +03:00
|
|
|
#include "mozilla/dom/MutationEventBinding.h"
|
2014-05-13 05:24:35 +04:00
|
|
|
#include "mozilla/dom/SVGGraphicsElementBinding.h"
|
2013-02-01 13:55:45 +04:00
|
|
|
#include "mozilla/dom/SVGMatrix.h"
|
2018-12-27 20:30:38 +03:00
|
|
|
#include "mozilla/dom/SVGRect.h"
|
2013-02-01 13:55:45 +04:00
|
|
|
#include "mozilla/dom/SVGSVGElement.h"
|
|
|
|
#include "nsContentUtils.h"
|
2013-01-06 10:25:54 +04:00
|
|
|
#include "nsIFrame.h"
|
2018-12-27 20:30:38 +03:00
|
|
|
#include "SVGContentUtils.h"
|
2017-02-09 21:24:31 +03:00
|
|
|
#include "nsSVGDisplayableFrame.h"
|
2013-01-06 10:25:54 +04:00
|
|
|
#include "nsSVGUtils.h"
|
2013-01-03 10:17:02 +04:00
|
|
|
|
2014-03-18 06:41:33 +04:00
|
|
|
using namespace mozilla::gfx;
|
|
|
|
|
2013-01-03 10:17:02 +04:00
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
2018-12-23 14:08:14 +03:00
|
|
|
already_AddRefed<DOMSVGAnimatedTransformList>
|
2013-01-03 10:17:02 +04:00
|
|
|
SVGTransformableElement::Transform() {
|
|
|
|
// We're creating a DOM wrapper, so we must tell GetAnimatedTransformList
|
2018-12-23 14:08:14 +03:00
|
|
|
// to allocate the DOMSVGAnimatedTransformList if it hasn't already done so:
|
|
|
|
return DOMSVGAnimatedTransformList::GetDOMWrapper(
|
2013-04-22 15:15:59 +04:00
|
|
|
GetAnimatedTransformList(DO_ALLOCATE), this);
|
2013-01-03 10:17:02 +04:00
|
|
|
}
|
|
|
|
|
2013-01-06 10:25:54 +04:00
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// nsIContent methods
|
|
|
|
|
|
|
|
NS_IMETHODIMP_(bool)
|
2017-10-03 01:05:19 +03:00
|
|
|
SVGTransformableElement::IsAttributeMapped(const nsAtom* name) const {
|
2013-01-06 10:25:54 +04:00
|
|
|
static const MappedAttributeEntry* const map[] = {sColorMap, sFillStrokeMap,
|
|
|
|
sGraphicsMap};
|
|
|
|
|
|
|
|
return FindAttributeDependence(name, map) ||
|
2018-12-21 11:58:14 +03:00
|
|
|
SVGElement::IsAttributeMapped(name);
|
2013-01-06 10:25:54 +04:00
|
|
|
}
|
|
|
|
|
2017-10-03 01:05:19 +03:00
|
|
|
nsChangeHint SVGTransformableElement::GetAttributeChangeHint(
|
2013-01-06 10:25:54 +04:00
|
|
|
const nsAtom* aAttribute, int32_t aModType) const {
|
|
|
|
nsChangeHint retval =
|
2018-12-21 11:58:14 +03:00
|
|
|
SVGElement::GetAttributeChangeHint(aAttribute, aModType);
|
2013-01-06 10:25:54 +04:00
|
|
|
if (aAttribute == nsGkAtoms::transform ||
|
|
|
|
aAttribute == nsGkAtoms::mozAnimateMotionDummyAttr) {
|
|
|
|
nsIFrame* frame =
|
|
|
|
const_cast<SVGTransformableElement*>(this)->GetPrimaryFrame();
|
2016-05-23 06:26:03 +03:00
|
|
|
retval |= nsChangeHint_InvalidateRenderingObservers;
|
2013-07-12 11:13:07 +04:00
|
|
|
if (!frame || (frame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
|
2014-11-04 17:52:27 +03:00
|
|
|
return retval;
|
2013-01-06 10:25:54 +04:00
|
|
|
}
|
2016-03-15 03:37:32 +03:00
|
|
|
|
|
|
|
bool isAdditionOrRemoval = false;
|
2018-06-26 00:20:54 +03:00
|
|
|
if (aModType == MutationEvent_Binding::ADDITION ||
|
|
|
|
aModType == MutationEvent_Binding::REMOVAL) {
|
2016-03-15 03:37:32 +03:00
|
|
|
isAdditionOrRemoval = true;
|
2013-01-06 10:25:54 +04:00
|
|
|
} else {
|
2018-06-26 00:20:54 +03:00
|
|
|
MOZ_ASSERT(aModType == MutationEvent_Binding::MODIFICATION,
|
2015-02-10 01:34:50 +03:00
|
|
|
"Unknown modification type.");
|
2018-01-28 00:39:08 +03:00
|
|
|
if (!mTransforms || !mTransforms->HasTransform()) {
|
|
|
|
// New value is empty, treat as removal.
|
|
|
|
isAdditionOrRemoval = true;
|
|
|
|
} else if (mTransforms->RequiresFrameReconstruction()) {
|
|
|
|
// Old value was empty, treat as addition.
|
2016-03-15 03:37:32 +03:00
|
|
|
isAdditionOrRemoval = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isAdditionOrRemoval) {
|
|
|
|
// Reconstruct the frame tree to handle stacking context changes:
|
2016-05-23 06:26:03 +03:00
|
|
|
retval |= nsChangeHint_ReconstructFrame;
|
2016-03-15 03:37:32 +03:00
|
|
|
} else {
|
2013-01-06 10:25:54 +04:00
|
|
|
// We just assume the old and new transforms are different.
|
2016-05-23 06:26:03 +03:00
|
|
|
retval |= nsChangeHint_UpdatePostTransformOverflow |
|
|
|
|
nsChangeHint_UpdateTransformLayer;
|
2013-01-06 10:25:54 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2017-10-03 01:05:19 +03:00
|
|
|
bool SVGTransformableElement::IsEventAttributeNameInternal(nsAtom* aName) {
|
2013-01-06 10:25:54 +04:00
|
|
|
return nsContentUtils::IsEventAttributeName(aName, EventNameType_SVGGraphic);
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
2018-12-21 11:58:14 +03:00
|
|
|
// SVGElement overrides
|
2013-01-06 10:25:54 +04:00
|
|
|
|
2017-02-23 11:35:08 +03:00
|
|
|
gfxMatrix SVGTransformableElement::PrependLocalTransformsTo(
|
|
|
|
const gfxMatrix& aMatrix, SVGTransformTypes aWhich) const {
|
|
|
|
if (aWhich == eChildToUserSpace) {
|
|
|
|
// We don't have any eUserSpaceToParent transforms. (Sub-classes that do
|
|
|
|
// must override this function and handle that themselves.)
|
|
|
|
return aMatrix;
|
|
|
|
}
|
|
|
|
return GetUserToParentTransform(mAnimateMotionTransform, mTransforms) *
|
|
|
|
aMatrix;
|
2013-01-06 10:25:54 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
const gfx::Matrix* SVGTransformableElement::GetAnimateMotionTransform() const {
|
|
|
|
return mAnimateMotionTransform.get();
|
|
|
|
}
|
|
|
|
|
2013-12-29 01:37:40 +04:00
|
|
|
void SVGTransformableElement::SetAnimateMotionTransform(
|
|
|
|
const gfx::Matrix* aMatrix) {
|
2013-01-06 10:25:54 +04:00
|
|
|
if ((!aMatrix && !mAnimateMotionTransform) ||
|
2018-02-03 00:14:08 +03:00
|
|
|
(aMatrix && mAnimateMotionTransform &&
|
|
|
|
aMatrix->FuzzyEquals(*mAnimateMotionTransform))) {
|
2013-01-06 10:25:54 +04:00
|
|
|
return;
|
|
|
|
}
|
2014-04-07 23:52:12 +04:00
|
|
|
bool transformSet = mTransforms && mTransforms->IsExplicitlySet();
|
|
|
|
bool prevSet = mAnimateMotionTransform || transformSet;
|
2013-12-29 01:37:40 +04:00
|
|
|
mAnimateMotionTransform = aMatrix ? new gfx::Matrix(*aMatrix) : nullptr;
|
2014-04-07 23:52:12 +04:00
|
|
|
bool nowSet = mAnimateMotionTransform || transformSet;
|
|
|
|
int32_t modType;
|
|
|
|
if (prevSet && !nowSet) {
|
2018-06-26 00:20:54 +03:00
|
|
|
modType = MutationEvent_Binding::REMOVAL;
|
2014-04-07 23:52:12 +04:00
|
|
|
} else if (!prevSet && nowSet) {
|
2018-06-26 00:20:54 +03:00
|
|
|
modType = MutationEvent_Binding::ADDITION;
|
2014-04-07 23:52:12 +04:00
|
|
|
} else {
|
2018-06-26 00:20:54 +03:00
|
|
|
modType = MutationEvent_Binding::MODIFICATION;
|
2014-04-07 23:52:12 +04:00
|
|
|
}
|
|
|
|
DidAnimateTransformList(modType);
|
2013-05-23 11:04:21 +04:00
|
|
|
nsIFrame* frame = GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
|
|
// If the result of this transform and any other transforms on this frame
|
|
|
|
// is the identity matrix, then DoApplyRenderingChangeToTree won't handle
|
|
|
|
// our nsChangeHint_UpdateTransformLayer hint since aFrame->IsTransformed()
|
|
|
|
// will return false. That's fine, but we still need to schedule a repaint,
|
|
|
|
// and that won't otherwise happen. Since it's cheap to call SchedulePaint,
|
|
|
|
// we don't bother to check IsTransformed().
|
|
|
|
frame->SchedulePaint();
|
|
|
|
}
|
2013-01-06 10:25:54 +04:00
|
|
|
}
|
|
|
|
|
2018-12-27 02:46:38 +03:00
|
|
|
SVGAnimatedTransformList* SVGTransformableElement::GetAnimatedTransformList(
|
2013-01-06 10:25:54 +04:00
|
|
|
uint32_t aFlags) {
|
|
|
|
if (!mTransforms && (aFlags & DO_ALLOCATE)) {
|
2018-12-27 02:46:38 +03:00
|
|
|
mTransforms = new SVGAnimatedTransformList();
|
2013-01-06 10:25:54 +04:00
|
|
|
}
|
|
|
|
return mTransforms;
|
|
|
|
}
|
|
|
|
|
2018-12-21 11:58:14 +03:00
|
|
|
SVGElement* SVGTransformableElement::GetNearestViewportElement() {
|
2013-02-01 13:55:45 +04:00
|
|
|
return SVGContentUtils::GetNearestViewportElement(this);
|
|
|
|
}
|
|
|
|
|
2018-12-21 11:58:14 +03:00
|
|
|
SVGElement* SVGTransformableElement::GetFarthestViewportElement() {
|
2013-02-01 13:55:45 +04:00
|
|
|
return SVGContentUtils::GetOuterSVGElement(this);
|
|
|
|
}
|
|
|
|
|
2017-07-06 15:00:35 +03:00
|
|
|
already_AddRefed<SVGIRect> SVGTransformableElement::GetBBox(
|
|
|
|
const SVGBoundingBoxOptions& aOptions, ErrorResult& rv) {
|
2017-01-05 10:31:56 +03:00
|
|
|
nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
|
2013-02-01 13:55:45 +04:00
|
|
|
|
2013-07-12 11:13:07 +04:00
|
|
|
if (!frame || (frame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
|
2013-02-01 13:55:45 +04:00
|
|
|
rv.Throw(NS_ERROR_FAILURE);
|
|
|
|
return nullptr;
|
|
|
|
}
|
2017-02-09 21:24:31 +03:00
|
|
|
nsSVGDisplayableFrame* svgframe = do_QueryFrame(frame);
|
2013-02-01 13:55:45 +04:00
|
|
|
if (!svgframe) {
|
|
|
|
rv.Throw(NS_ERROR_NOT_IMPLEMENTED); // XXX: outer svg
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2014-05-13 05:24:35 +04:00
|
|
|
if (!NS_SVGNewGetBBoxEnabled()) {
|
2017-08-29 15:41:45 +03:00
|
|
|
return NS_NewSVGRect(
|
|
|
|
this, ToRect(nsSVGUtils::GetBBox(
|
|
|
|
frame, nsSVGUtils::eBBoxIncludeFillGeometry |
|
|
|
|
nsSVGUtils::eUseUserSpaceOfUseElement)));
|
2014-05-13 05:24:35 +04:00
|
|
|
} else {
|
2017-09-01 17:37:59 +03:00
|
|
|
uint32_t flags = 0;
|
2014-05-13 05:24:35 +04:00
|
|
|
if (aOptions.mFill) {
|
2015-04-22 19:14:27 +03:00
|
|
|
flags |= nsSVGUtils::eBBoxIncludeFill;
|
2014-05-13 05:24:35 +04:00
|
|
|
}
|
|
|
|
if (aOptions.mStroke) {
|
2015-04-22 19:14:27 +03:00
|
|
|
flags |= nsSVGUtils::eBBoxIncludeStroke;
|
2014-05-13 05:24:35 +04:00
|
|
|
}
|
|
|
|
if (aOptions.mMarkers) {
|
2015-04-22 19:14:27 +03:00
|
|
|
flags |= nsSVGUtils::eBBoxIncludeMarkers;
|
2014-05-13 05:24:35 +04:00
|
|
|
}
|
|
|
|
if (aOptions.mClipped) {
|
2015-04-22 19:14:27 +03:00
|
|
|
flags |= nsSVGUtils::eBBoxIncludeClipped;
|
2014-05-13 05:24:35 +04:00
|
|
|
}
|
2017-09-01 17:37:59 +03:00
|
|
|
if (flags == 0) {
|
|
|
|
return NS_NewSVGRect(this, 0, 0, 0, 0);
|
|
|
|
}
|
2015-04-22 19:14:27 +03:00
|
|
|
if (flags == nsSVGUtils::eBBoxIncludeMarkers ||
|
|
|
|
flags == nsSVGUtils::eBBoxIncludeClipped) {
|
|
|
|
flags |= nsSVGUtils::eBBoxIncludeFill;
|
2014-05-13 05:24:35 +04:00
|
|
|
}
|
2017-09-01 17:37:59 +03:00
|
|
|
flags |= nsSVGUtils::eUseUserSpaceOfUseElement;
|
2015-04-22 19:14:27 +03:00
|
|
|
return NS_NewSVGRect(this, ToRect(nsSVGUtils::GetBBox(frame, flags)));
|
2014-05-13 05:24:35 +04:00
|
|
|
}
|
2013-02-01 13:55:45 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<SVGMatrix> SVGTransformableElement::GetCTM() {
|
2019-01-02 16:05:23 +03:00
|
|
|
Document* currentDoc = GetComposedDoc();
|
2013-02-01 13:55:45 +04:00
|
|
|
if (currentDoc) {
|
|
|
|
// Flush all pending notifications so that our frames are up to date
|
2017-01-05 10:31:56 +03:00
|
|
|
currentDoc->FlushPendingNotifications(FlushType::Layout);
|
2013-02-01 13:55:45 +04:00
|
|
|
}
|
2013-12-31 22:44:27 +04:00
|
|
|
gfx::Matrix m = SVGContentUtils::GetCTM(this, false);
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<SVGMatrix> mat =
|
|
|
|
m.IsSingular() ? nullptr : new SVGMatrix(ThebesMatrix(m));
|
2013-02-01 13:55:45 +04:00
|
|
|
return mat.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<SVGMatrix> SVGTransformableElement::GetScreenCTM() {
|
2019-01-02 16:05:23 +03:00
|
|
|
Document* currentDoc = GetComposedDoc();
|
2013-02-01 13:55:45 +04:00
|
|
|
if (currentDoc) {
|
|
|
|
// Flush all pending notifications so that our frames are up to date
|
2017-01-05 10:31:56 +03:00
|
|
|
currentDoc->FlushPendingNotifications(FlushType::Layout);
|
2013-02-01 13:55:45 +04:00
|
|
|
}
|
2013-12-31 22:44:27 +04:00
|
|
|
gfx::Matrix m = SVGContentUtils::GetCTM(this, true);
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<SVGMatrix> mat =
|
|
|
|
m.IsSingular() ? nullptr : new SVGMatrix(ThebesMatrix(m));
|
2013-02-01 13:55:45 +04:00
|
|
|
return mat.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<SVGMatrix> SVGTransformableElement::GetTransformToElement(
|
|
|
|
SVGGraphicsElement& aElement, ErrorResult& rv) {
|
|
|
|
// the easiest way to do this (if likely to increase rounding error):
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<SVGMatrix> ourScreenCTM = GetScreenCTM();
|
|
|
|
RefPtr<SVGMatrix> targetScreenCTM = aElement.GetScreenCTM();
|
2013-02-01 13:55:45 +04:00
|
|
|
if (!ourScreenCTM || !targetScreenCTM) {
|
|
|
|
rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
|
|
return nullptr;
|
|
|
|
}
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<SVGMatrix> tmp = targetScreenCTM->Inverse(rv);
|
2013-02-01 13:55:45 +04:00
|
|
|
if (rv.Failed()) return nullptr;
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<SVGMatrix> mat = tmp->Multiply(*ourScreenCTM);
|
2013-02-01 13:55:45 +04:00
|
|
|
return mat.forget();
|
|
|
|
}
|
|
|
|
|
2017-02-23 11:35:08 +03:00
|
|
|
/* static */ gfxMatrix SVGTransformableElement::GetUserToParentTransform(
|
|
|
|
const gfx::Matrix* aAnimateMotionTransform,
|
2018-12-27 02:46:38 +03:00
|
|
|
const SVGAnimatedTransformList* aTransforms) {
|
2017-02-23 11:35:08 +03:00
|
|
|
gfxMatrix result;
|
|
|
|
|
|
|
|
if (aAnimateMotionTransform) {
|
|
|
|
result.PreMultiply(ThebesMatrix(*aAnimateMotionTransform));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aTransforms) {
|
|
|
|
result.PreMultiply(aTransforms->GetAnimValue().GetConsolidationMatrix());
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-01-03 10:17:02 +04:00
|
|
|
} // namespace dom
|
|
|
|
} // namespace mozilla
|