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