Bug 1700051: part 46) Const-qualify `mozInlineSpellStatus::mAnchorRange`. r=smaug
[gecko.git] / dom / svg / SVGTransformableElement.cpp
blob26558e7954651a158664280c4639d78e90c55702
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "SVGTransformableElement.h"
9 #include "DOMSVGAnimatedTransformList.h"
10 #include "gfx2DGlue.h"
11 #include "mozilla/dom/MutationEventBinding.h"
12 #include "mozilla/dom/SVGGraphicsElementBinding.h"
13 #include "mozilla/dom/SVGMatrix.h"
14 #include "mozilla/dom/SVGRect.h"
15 #include "mozilla/dom/SVGSVGElement.h"
16 #include "mozilla/ISVGDisplayableFrame.h"
17 #include "mozilla/SVGContentUtils.h"
18 #include "mozilla/SVGTextFrame.h"
19 #include "mozilla/SVGUtils.h"
20 #include "nsContentUtils.h"
21 #include "nsIFrame.h"
22 #include "nsLayoutUtils.h"
24 using namespace mozilla::gfx;
26 namespace mozilla {
27 namespace dom {
29 already_AddRefed<DOMSVGAnimatedTransformList>
30 SVGTransformableElement::Transform() {
31 // We're creating a DOM wrapper, so we must tell GetAnimatedTransformList
32 // to allocate the DOMSVGAnimatedTransformList if it hasn't already done so:
33 return DOMSVGAnimatedTransformList::GetDOMWrapper(
34 GetAnimatedTransformList(DO_ALLOCATE), this);
37 //----------------------------------------------------------------------
38 // nsIContent methods
40 NS_IMETHODIMP_(bool)
41 SVGTransformableElement::IsAttributeMapped(const nsAtom* name) const {
42 static const MappedAttributeEntry* const map[] = {sColorMap, sFillStrokeMap,
43 sGraphicsMap};
45 return FindAttributeDependence(name, map) ||
46 SVGElement::IsAttributeMapped(name);
49 nsChangeHint SVGTransformableElement::GetAttributeChangeHint(
50 const nsAtom* aAttribute, int32_t aModType) const {
51 nsChangeHint retval =
52 SVGElement::GetAttributeChangeHint(aAttribute, aModType);
53 if (aAttribute == nsGkAtoms::transform ||
54 aAttribute == nsGkAtoms::mozAnimateMotionDummyAttr) {
55 nsIFrame* frame =
56 const_cast<SVGTransformableElement*>(this)->GetPrimaryFrame();
57 retval |= nsChangeHint_InvalidateRenderingObservers;
58 if (!frame || (frame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
59 return retval;
62 bool isAdditionOrRemoval = false;
63 if (aModType == MutationEvent_Binding::ADDITION ||
64 aModType == MutationEvent_Binding::REMOVAL) {
65 isAdditionOrRemoval = true;
66 } else {
67 MOZ_ASSERT(aModType == MutationEvent_Binding::MODIFICATION,
68 "Unknown modification type.");
69 if (!mTransforms || !mTransforms->HasTransform()) {
70 // New value is empty, treat as removal.
71 // FIXME: Should we just rely on CreatedOrRemovedOnLastChange?
72 isAdditionOrRemoval = true;
73 } else if (mTransforms->CreatedOrRemovedOnLastChange()) {
74 // Old value was empty, treat as addition.
75 isAdditionOrRemoval = true;
79 if (isAdditionOrRemoval) {
80 retval |= nsChangeHint_ComprehensiveAddOrRemoveTransform;
81 } else {
82 // We just assume the old and new transforms are different.
83 retval |= nsChangeHint_UpdatePostTransformOverflow |
84 nsChangeHint_UpdateTransformLayer;
87 return retval;
90 bool SVGTransformableElement::IsEventAttributeNameInternal(nsAtom* aName) {
91 return nsContentUtils::IsEventAttributeName(aName, EventNameType_SVGGraphic);
94 //----------------------------------------------------------------------
95 // SVGElement overrides
97 gfxMatrix SVGTransformableElement::PrependLocalTransformsTo(
98 const gfxMatrix& aMatrix, SVGTransformTypes aWhich) const {
99 if (aWhich == eChildToUserSpace) {
100 // We don't have any eUserSpaceToParent transforms. (Sub-classes that do
101 // must override this function and handle that themselves.)
102 return aMatrix;
104 return GetUserToParentTransform(mAnimateMotionTransform.get(),
105 mTransforms.get()) *
106 aMatrix;
109 const gfx::Matrix* SVGTransformableElement::GetAnimateMotionTransform() const {
110 return mAnimateMotionTransform.get();
113 void SVGTransformableElement::SetAnimateMotionTransform(
114 const gfx::Matrix* aMatrix) {
115 if ((!aMatrix && !mAnimateMotionTransform) ||
116 (aMatrix && mAnimateMotionTransform &&
117 aMatrix->FuzzyEquals(*mAnimateMotionTransform))) {
118 return;
120 bool transformSet = mTransforms && mTransforms->IsExplicitlySet();
121 bool prevSet = mAnimateMotionTransform || transformSet;
122 mAnimateMotionTransform =
123 aMatrix ? MakeUnique<gfx::Matrix>(*aMatrix) : nullptr;
124 bool nowSet = mAnimateMotionTransform || transformSet;
125 int32_t modType;
126 if (prevSet && !nowSet) {
127 modType = MutationEvent_Binding::REMOVAL;
128 } else if (!prevSet && nowSet) {
129 modType = MutationEvent_Binding::ADDITION;
130 } else {
131 modType = MutationEvent_Binding::MODIFICATION;
133 DidAnimateTransformList(modType);
134 nsIFrame* frame = GetPrimaryFrame();
135 if (frame) {
136 // If the result of this transform and any other transforms on this frame
137 // is the identity matrix, then DoApplyRenderingChangeToTree won't handle
138 // our nsChangeHint_UpdateTransformLayer hint since aFrame->IsTransformed()
139 // will return false. That's fine, but we still need to schedule a repaint,
140 // and that won't otherwise happen. Since it's cheap to call SchedulePaint,
141 // we don't bother to check IsTransformed().
142 frame->SchedulePaint();
146 SVGAnimatedTransformList* SVGTransformableElement::GetAnimatedTransformList(
147 uint32_t aFlags) {
148 if (!mTransforms && (aFlags & DO_ALLOCATE)) {
149 mTransforms = MakeUnique<SVGAnimatedTransformList>();
151 return mTransforms.get();
154 SVGElement* SVGTransformableElement::GetNearestViewportElement() {
155 return SVGContentUtils::GetNearestViewportElement(this);
158 SVGElement* SVGTransformableElement::GetFarthestViewportElement() {
159 return SVGContentUtils::GetOuterSVGElement(this);
162 static already_AddRefed<SVGRect> ZeroBBox(SVGTransformableElement& aOwner) {
163 return MakeAndAddRef<SVGRect>(&aOwner, Rect{0, 0, 0, 0});
166 already_AddRefed<SVGRect> SVGTransformableElement::GetBBox(
167 const SVGBoundingBoxOptions& aOptions) {
168 nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
170 if (!frame || (frame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
171 return ZeroBBox(*this);
173 ISVGDisplayableFrame* svgframe = do_QueryFrame(frame);
175 if (!svgframe) {
176 if (!SVGUtils::IsInSVGTextSubtree(frame)) {
177 return ZeroBBox(*this);
180 // For <tspan>, <textPath>, the frame is an nsInlineFrame or
181 // nsBlockFrame, |svgframe| will be a nullptr.
182 // We implement their getBBox directly here instead of in
183 // SVGUtils::GetBBox, because SVGUtils::GetBBox is more
184 // or less used for other purpose elsewhere. e.g. gradient
185 // code assumes GetBBox of <tspan> returns the bbox of the
186 // outer <text>.
187 // TODO: cleanup this sort of usecase of SVGUtils::GetBBox,
188 // then move this code SVGUtils::GetBBox.
189 SVGTextFrame* text =
190 static_cast<SVGTextFrame*>(nsLayoutUtils::GetClosestFrameOfType(
191 frame->GetParent(), LayoutFrameType::SVGText));
193 if (text->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
194 return ZeroBBox(*this);
197 gfxRect rec = text->TransformFrameRectFromTextChild(
198 frame->GetRectRelativeToSelf(), frame);
200 // Should also add the |x|, |y| of the SVGTextFrame itself, since
201 // the result obtained by TransformFrameRectFromTextChild doesn't
202 // include them.
203 rec.x += float(text->GetPosition().x) / AppUnitsPerCSSPixel();
204 rec.y += float(text->GetPosition().y) / AppUnitsPerCSSPixel();
206 return do_AddRef(new SVGRect(this, ToRect(rec)));
209 if (!NS_SVGNewGetBBoxEnabled()) {
210 return do_AddRef(new SVGRect(
211 this, ToRect(SVGUtils::GetBBox(
212 frame, SVGUtils::eBBoxIncludeFillGeometry |
213 SVGUtils::eUseUserSpaceOfUseElement))));
215 uint32_t flags = 0;
216 if (aOptions.mFill) {
217 flags |= SVGUtils::eBBoxIncludeFill;
219 if (aOptions.mStroke) {
220 flags |= SVGUtils::eBBoxIncludeStroke;
222 if (aOptions.mMarkers) {
223 flags |= SVGUtils::eBBoxIncludeMarkers;
225 if (aOptions.mClipped) {
226 flags |= SVGUtils::eBBoxIncludeClipped;
228 if (flags == 0) {
229 return do_AddRef(new SVGRect(this, gfx::Rect()));
231 if (flags == SVGUtils::eBBoxIncludeMarkers ||
232 flags == SVGUtils::eBBoxIncludeClipped) {
233 flags |= SVGUtils::eBBoxIncludeFill;
235 flags |= SVGUtils::eUseUserSpaceOfUseElement;
236 return do_AddRef(new SVGRect(this, ToRect(SVGUtils::GetBBox(frame, flags))));
239 already_AddRefed<SVGMatrix> SVGTransformableElement::GetCTM() {
240 Document* currentDoc = GetComposedDoc();
241 if (currentDoc) {
242 // Flush all pending notifications so that our frames are up to date
243 currentDoc->FlushPendingNotifications(FlushType::Layout);
245 gfx::Matrix m = SVGContentUtils::GetCTM(this, false);
246 RefPtr<SVGMatrix> mat =
247 m.IsSingular() ? nullptr : new SVGMatrix(ThebesMatrix(m));
248 return mat.forget();
251 already_AddRefed<SVGMatrix> SVGTransformableElement::GetScreenCTM() {
252 Document* currentDoc = GetComposedDoc();
253 if (currentDoc) {
254 // Flush all pending notifications so that our frames are up to date
255 currentDoc->FlushPendingNotifications(FlushType::Layout);
257 gfx::Matrix m = SVGContentUtils::GetCTM(this, true);
258 RefPtr<SVGMatrix> mat =
259 m.IsSingular() ? nullptr : new SVGMatrix(ThebesMatrix(m));
260 return mat.forget();
263 already_AddRefed<SVGMatrix> SVGTransformableElement::GetTransformToElement(
264 SVGGraphicsElement& aElement, ErrorResult& rv) {
265 // the easiest way to do this (if likely to increase rounding error):
266 RefPtr<SVGMatrix> ourScreenCTM = GetScreenCTM();
267 RefPtr<SVGMatrix> targetScreenCTM = aElement.GetScreenCTM();
268 if (!ourScreenCTM || !targetScreenCTM) {
269 rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
270 return nullptr;
272 RefPtr<SVGMatrix> tmp = targetScreenCTM->Inverse(rv);
273 if (rv.Failed()) return nullptr;
275 RefPtr<SVGMatrix> mat = tmp->Multiply(*ourScreenCTM);
276 return mat.forget();
279 /* static */
280 gfxMatrix SVGTransformableElement::GetUserToParentTransform(
281 const gfx::Matrix* aAnimateMotionTransform,
282 const SVGAnimatedTransformList* aTransforms) {
283 gfxMatrix result;
285 if (aAnimateMotionTransform) {
286 result.PreMultiply(ThebesMatrix(*aAnimateMotionTransform));
289 if (aTransforms) {
290 result.PreMultiply(aTransforms->GetAnimValue().GetConsolidationMatrix());
293 return result;
296 } // namespace dom
297 } // namespace mozilla