/src/mozilla-central/dom/svg/SVGTransformableElement.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #include "gfx2DGlue.h" |
8 | | #include "mozilla/dom/MutationEventBinding.h" |
9 | | #include "mozilla/dom/SVGAnimatedTransformList.h" |
10 | | #include "mozilla/dom/SVGGraphicsElementBinding.h" |
11 | | #include "mozilla/dom/SVGTransformableElement.h" |
12 | | #include "mozilla/dom/SVGMatrix.h" |
13 | | #include "mozilla/dom/SVGSVGElement.h" |
14 | | #include "nsContentUtils.h" |
15 | | #include "nsIFrame.h" |
16 | | #include "nsSVGDisplayableFrame.h" |
17 | | #include "mozilla/dom/SVGRect.h" |
18 | | #include "nsSVGUtils.h" |
19 | | #include "SVGContentUtils.h" |
20 | | |
21 | | using namespace mozilla::gfx; |
22 | | |
23 | | namespace mozilla { |
24 | | namespace dom { |
25 | | |
26 | | already_AddRefed<SVGAnimatedTransformList> |
27 | | SVGTransformableElement::Transform() |
28 | 0 | { |
29 | 0 | // We're creating a DOM wrapper, so we must tell GetAnimatedTransformList |
30 | 0 | // to allocate the SVGAnimatedTransformList if it hasn't already done so: |
31 | 0 | return SVGAnimatedTransformList::GetDOMWrapper( |
32 | 0 | GetAnimatedTransformList(DO_ALLOCATE), this); |
33 | 0 |
|
34 | 0 | } |
35 | | |
36 | | //---------------------------------------------------------------------- |
37 | | // nsIContent methods |
38 | | |
39 | | NS_IMETHODIMP_(bool) |
40 | | SVGTransformableElement::IsAttributeMapped(const nsAtom* name) const |
41 | 0 | { |
42 | 0 | static const MappedAttributeEntry* const map[] = { |
43 | 0 | sColorMap, |
44 | 0 | sFillStrokeMap, |
45 | 0 | sGraphicsMap |
46 | 0 | }; |
47 | 0 |
|
48 | 0 | return FindAttributeDependence(name, map) || |
49 | 0 | nsSVGElement::IsAttributeMapped(name); |
50 | 0 | } |
51 | | |
52 | | nsChangeHint |
53 | | SVGTransformableElement::GetAttributeChangeHint(const nsAtom* aAttribute, |
54 | | int32_t aModType) const |
55 | 0 | { |
56 | 0 | nsChangeHint retval = |
57 | 0 | nsSVGElement::GetAttributeChangeHint(aAttribute, aModType); |
58 | 0 | if (aAttribute == nsGkAtoms::transform || |
59 | 0 | aAttribute == nsGkAtoms::mozAnimateMotionDummyAttr) { |
60 | 0 | nsIFrame* frame = |
61 | 0 | const_cast<SVGTransformableElement*>(this)->GetPrimaryFrame(); |
62 | 0 | retval |= nsChangeHint_InvalidateRenderingObservers; |
63 | 0 | if (!frame || (frame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) { |
64 | 0 | return retval; |
65 | 0 | } |
66 | 0 | |
67 | 0 | bool isAdditionOrRemoval = false; |
68 | 0 | if (aModType == MutationEvent_Binding::ADDITION || |
69 | 0 | aModType == MutationEvent_Binding::REMOVAL) { |
70 | 0 | isAdditionOrRemoval = true; |
71 | 0 | } else { |
72 | 0 | MOZ_ASSERT(aModType == MutationEvent_Binding::MODIFICATION, |
73 | 0 | "Unknown modification type."); |
74 | 0 | if (!mTransforms || |
75 | 0 | !mTransforms->HasTransform()) { |
76 | 0 | // New value is empty, treat as removal. |
77 | 0 | isAdditionOrRemoval = true; |
78 | 0 | } else if (mTransforms->RequiresFrameReconstruction()) { |
79 | 0 | // Old value was empty, treat as addition. |
80 | 0 | isAdditionOrRemoval = true; |
81 | 0 | } |
82 | 0 | } |
83 | 0 |
|
84 | 0 | if (isAdditionOrRemoval) { |
85 | 0 | // Reconstruct the frame tree to handle stacking context changes: |
86 | 0 | retval |= nsChangeHint_ReconstructFrame; |
87 | 0 | } else { |
88 | 0 | // We just assume the old and new transforms are different. |
89 | 0 | retval |= nsChangeHint_UpdatePostTransformOverflow | |
90 | 0 | nsChangeHint_UpdateTransformLayer; |
91 | 0 | } |
92 | 0 | } |
93 | 0 | return retval; |
94 | 0 | } |
95 | | |
96 | | bool |
97 | | SVGTransformableElement::IsEventAttributeNameInternal(nsAtom* aName) |
98 | 0 | { |
99 | 0 | return nsContentUtils::IsEventAttributeName(aName, EventNameType_SVGGraphic); |
100 | 0 | } |
101 | | |
102 | | //---------------------------------------------------------------------- |
103 | | // nsSVGElement overrides |
104 | | |
105 | | gfxMatrix |
106 | | SVGTransformableElement::PrependLocalTransformsTo(const gfxMatrix& aMatrix, |
107 | | SVGTransformTypes aWhich) const |
108 | 0 | { |
109 | 0 | if (aWhich == eChildToUserSpace) { |
110 | 0 | // We don't have any eUserSpaceToParent transforms. (Sub-classes that do |
111 | 0 | // must override this function and handle that themselves.) |
112 | 0 | return aMatrix; |
113 | 0 | } |
114 | 0 | return GetUserToParentTransform(mAnimateMotionTransform, mTransforms) * aMatrix; |
115 | 0 | } |
116 | | |
117 | | const gfx::Matrix* |
118 | | SVGTransformableElement::GetAnimateMotionTransform() const |
119 | 0 | { |
120 | 0 | return mAnimateMotionTransform.get(); |
121 | 0 | } |
122 | | |
123 | | void |
124 | | SVGTransformableElement::SetAnimateMotionTransform(const gfx::Matrix* aMatrix) |
125 | 0 | { |
126 | 0 | if ((!aMatrix && !mAnimateMotionTransform) || |
127 | 0 | (aMatrix && mAnimateMotionTransform && aMatrix->FuzzyEquals(*mAnimateMotionTransform))) { |
128 | 0 | return; |
129 | 0 | } |
130 | 0 | bool transformSet = mTransforms && mTransforms->IsExplicitlySet(); |
131 | 0 | bool prevSet = mAnimateMotionTransform || transformSet; |
132 | 0 | mAnimateMotionTransform = aMatrix ? new gfx::Matrix(*aMatrix) : nullptr; |
133 | 0 | bool nowSet = mAnimateMotionTransform || transformSet; |
134 | 0 | int32_t modType; |
135 | 0 | if (prevSet && !nowSet) { |
136 | 0 | modType = MutationEvent_Binding::REMOVAL; |
137 | 0 | } else if(!prevSet && nowSet) { |
138 | 0 | modType = MutationEvent_Binding::ADDITION; |
139 | 0 | } else { |
140 | 0 | modType = MutationEvent_Binding::MODIFICATION; |
141 | 0 | } |
142 | 0 | DidAnimateTransformList(modType); |
143 | 0 | nsIFrame* frame = GetPrimaryFrame(); |
144 | 0 | if (frame) { |
145 | 0 | // If the result of this transform and any other transforms on this frame |
146 | 0 | // is the identity matrix, then DoApplyRenderingChangeToTree won't handle |
147 | 0 | // our nsChangeHint_UpdateTransformLayer hint since aFrame->IsTransformed() |
148 | 0 | // will return false. That's fine, but we still need to schedule a repaint, |
149 | 0 | // and that won't otherwise happen. Since it's cheap to call SchedulePaint, |
150 | 0 | // we don't bother to check IsTransformed(). |
151 | 0 | frame->SchedulePaint(); |
152 | 0 | } |
153 | 0 | } |
154 | | |
155 | | nsSVGAnimatedTransformList* |
156 | | SVGTransformableElement::GetAnimatedTransformList(uint32_t aFlags) |
157 | 0 | { |
158 | 0 | if (!mTransforms && (aFlags & DO_ALLOCATE)) { |
159 | 0 | mTransforms = new nsSVGAnimatedTransformList(); |
160 | 0 | } |
161 | 0 | return mTransforms; |
162 | 0 | } |
163 | | |
164 | | nsSVGElement* |
165 | | SVGTransformableElement::GetNearestViewportElement() |
166 | 0 | { |
167 | 0 | return SVGContentUtils::GetNearestViewportElement(this); |
168 | 0 | } |
169 | | |
170 | | nsSVGElement* |
171 | | SVGTransformableElement::GetFarthestViewportElement() |
172 | 0 | { |
173 | 0 | return SVGContentUtils::GetOuterSVGElement(this); |
174 | 0 | } |
175 | | |
176 | | already_AddRefed<SVGIRect> |
177 | | SVGTransformableElement::GetBBox(const SVGBoundingBoxOptions& aOptions, |
178 | | ErrorResult& rv) |
179 | 0 | { |
180 | 0 | nsIFrame* frame = GetPrimaryFrame(FlushType::Layout); |
181 | 0 |
|
182 | 0 | if (!frame || (frame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) { |
183 | 0 | rv.Throw(NS_ERROR_FAILURE); |
184 | 0 | return nullptr; |
185 | 0 | } |
186 | 0 | nsSVGDisplayableFrame* svgframe = do_QueryFrame(frame); |
187 | 0 | if (!svgframe) { |
188 | 0 | rv.Throw(NS_ERROR_NOT_IMPLEMENTED); // XXX: outer svg |
189 | 0 | return nullptr; |
190 | 0 | } |
191 | 0 | |
192 | 0 | if (!NS_SVGNewGetBBoxEnabled()) { |
193 | 0 | return NS_NewSVGRect(this, ToRect(nsSVGUtils::GetBBox(frame, |
194 | 0 | nsSVGUtils::eBBoxIncludeFillGeometry | |
195 | 0 | nsSVGUtils::eUseUserSpaceOfUseElement))); |
196 | 0 | } else { |
197 | 0 | uint32_t flags = 0; |
198 | 0 | if (aOptions.mFill) { |
199 | 0 | flags |= nsSVGUtils::eBBoxIncludeFill; |
200 | 0 | } |
201 | 0 | if (aOptions.mStroke) { |
202 | 0 | flags |= nsSVGUtils::eBBoxIncludeStroke; |
203 | 0 | } |
204 | 0 | if (aOptions.mMarkers) { |
205 | 0 | flags |= nsSVGUtils::eBBoxIncludeMarkers; |
206 | 0 | } |
207 | 0 | if (aOptions.mClipped) { |
208 | 0 | flags |= nsSVGUtils::eBBoxIncludeClipped; |
209 | 0 | } |
210 | 0 | if (flags == 0) { |
211 | 0 | return NS_NewSVGRect(this,0,0,0,0); |
212 | 0 | } |
213 | 0 | if (flags == nsSVGUtils::eBBoxIncludeMarkers || |
214 | 0 | flags == nsSVGUtils::eBBoxIncludeClipped) { |
215 | 0 | flags |= nsSVGUtils::eBBoxIncludeFill; |
216 | 0 | } |
217 | 0 | flags |= nsSVGUtils::eUseUserSpaceOfUseElement; |
218 | 0 | return NS_NewSVGRect(this, ToRect(nsSVGUtils::GetBBox(frame, flags))); |
219 | 0 | } |
220 | 0 | } |
221 | | |
222 | | already_AddRefed<SVGMatrix> |
223 | | SVGTransformableElement::GetCTM() |
224 | 0 | { |
225 | 0 | nsIDocument* currentDoc = GetComposedDoc(); |
226 | 0 | if (currentDoc) { |
227 | 0 | // Flush all pending notifications so that our frames are up to date |
228 | 0 | currentDoc->FlushPendingNotifications(FlushType::Layout); |
229 | 0 | } |
230 | 0 | gfx::Matrix m = SVGContentUtils::GetCTM(this, false); |
231 | 0 | RefPtr<SVGMatrix> mat = m.IsSingular() ? nullptr : new SVGMatrix(ThebesMatrix(m)); |
232 | 0 | return mat.forget(); |
233 | 0 | } |
234 | | |
235 | | already_AddRefed<SVGMatrix> |
236 | | SVGTransformableElement::GetScreenCTM() |
237 | 0 | { |
238 | 0 | nsIDocument* currentDoc = GetComposedDoc(); |
239 | 0 | if (currentDoc) { |
240 | 0 | // Flush all pending notifications so that our frames are up to date |
241 | 0 | currentDoc->FlushPendingNotifications(FlushType::Layout); |
242 | 0 | } |
243 | 0 | gfx::Matrix m = SVGContentUtils::GetCTM(this, true); |
244 | 0 | RefPtr<SVGMatrix> mat = m.IsSingular() ? nullptr : new SVGMatrix(ThebesMatrix(m)); |
245 | 0 | return mat.forget(); |
246 | 0 | } |
247 | | |
248 | | already_AddRefed<SVGMatrix> |
249 | | SVGTransformableElement::GetTransformToElement(SVGGraphicsElement& aElement, |
250 | | ErrorResult& rv) |
251 | 0 | { |
252 | 0 | // the easiest way to do this (if likely to increase rounding error): |
253 | 0 | RefPtr<SVGMatrix> ourScreenCTM = GetScreenCTM(); |
254 | 0 | RefPtr<SVGMatrix> targetScreenCTM = aElement.GetScreenCTM(); |
255 | 0 | if (!ourScreenCTM || !targetScreenCTM) { |
256 | 0 | rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); |
257 | 0 | return nullptr; |
258 | 0 | } |
259 | 0 | RefPtr<SVGMatrix> tmp = targetScreenCTM->Inverse(rv); |
260 | 0 | if (rv.Failed()) return nullptr; |
261 | 0 | |
262 | 0 | RefPtr<SVGMatrix> mat = tmp->Multiply(*ourScreenCTM); |
263 | 0 | return mat.forget(); |
264 | 0 | } |
265 | | |
266 | | /* static */ gfxMatrix |
267 | | SVGTransformableElement::GetUserToParentTransform( |
268 | | const gfx::Matrix* aAnimateMotionTransform, |
269 | | const nsSVGAnimatedTransformList* aTransforms) |
270 | 0 | { |
271 | 0 | gfxMatrix result; |
272 | 0 |
|
273 | 0 | if (aAnimateMotionTransform) { |
274 | 0 | result.PreMultiply(ThebesMatrix(*aAnimateMotionTransform)); |
275 | 0 | } |
276 | 0 |
|
277 | 0 | if (aTransforms) { |
278 | 0 | result.PreMultiply(aTransforms->GetAnimValue().GetConsolidationMatrix()); |
279 | 0 | } |
280 | 0 |
|
281 | 0 | return result; |
282 | 0 | } |
283 | | |
284 | | } // namespace dom |
285 | | } // namespace mozilla |
286 | | |