Source code

Revision control

Other Tools

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