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 "mozilla/ArrayUtils.h"
8 :
9 : #include "nsSVGLength2.h"
10 : #include "mozilla/dom/SVGAnimatedLength.h"
11 : #include "mozilla/dom/SVGSVGElement.h"
12 : #include "nsContentUtils.h" // NS_ENSURE_FINITE
13 : #include "nsIFrame.h"
14 : #include "nsSMILFloatType.h"
15 : #include "nsSMILValue.h"
16 : #include "nsSVGAttrTearoffTable.h"
17 : #include "nsSVGIntegrationUtils.h"
18 : #include "nsTextFormatter.h"
19 : #include "DOMSVGLength.h"
20 : #include "LayoutLogging.h"
21 :
22 : using namespace mozilla;
23 : using namespace mozilla::dom;
24 :
25 : static nsIAtom** const unitMap[] =
26 : {
27 : nullptr, /* SVG_LENGTHTYPE_UNKNOWN */
28 : nullptr, /* SVG_LENGTHTYPE_NUMBER */
29 : &nsGkAtoms::percentage,
30 : &nsGkAtoms::em,
31 : &nsGkAtoms::ex,
32 : &nsGkAtoms::px,
33 : &nsGkAtoms::cm,
34 : &nsGkAtoms::mm,
35 : &nsGkAtoms::in,
36 : &nsGkAtoms::pt,
37 : &nsGkAtoms::pc
38 : };
39 :
40 : static nsSVGAttrTearoffTable<nsSVGLength2, SVGAnimatedLength>
41 3 : sSVGAnimatedLengthTearoffTable;
42 :
43 : /* Helper functions */
44 :
45 : static bool
46 106 : IsValidUnitType(uint16_t unit)
47 : {
48 212 : if (unit > nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN &&
49 106 : unit <= nsIDOMSVGLength::SVG_LENGTHTYPE_PC)
50 106 : return true;
51 :
52 0 : return false;
53 : }
54 :
55 : static void
56 0 : GetUnitString(nsAString& unit, uint16_t unitType)
57 : {
58 0 : if (IsValidUnitType(unitType)) {
59 0 : if (unitMap[unitType]) {
60 0 : (*unitMap[unitType])->ToString(unit);
61 : }
62 0 : return;
63 : }
64 :
65 0 : NS_NOTREACHED("Unknown unit type");
66 0 : return;
67 : }
68 :
69 : static uint16_t
70 106 : GetUnitTypeForString(const nsAString& unitStr)
71 : {
72 106 : if (unitStr.IsEmpty())
73 100 : return nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER;
74 :
75 6 : nsIAtom *unitAtom = NS_GetStaticAtom(unitStr);
76 6 : if (unitAtom) {
77 36 : for (uint32_t i = 0 ; i < ArrayLength(unitMap) ; i++) {
78 36 : if (unitMap[i] && *unitMap[i] == unitAtom) {
79 6 : return i;
80 : }
81 : }
82 : }
83 :
84 0 : return nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN;
85 : }
86 :
87 : static void
88 0 : GetValueString(nsAString &aValueAsString, float aValue, uint16_t aUnitType)
89 : {
90 : char16_t buf[24];
91 0 : nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
92 : u"%g",
93 0 : (double)aValue);
94 0 : aValueAsString.Assign(buf);
95 :
96 0 : nsAutoString unitString;
97 0 : GetUnitString(unitString, aUnitType);
98 0 : aValueAsString.Append(unitString);
99 0 : }
100 :
101 : static bool
102 106 : GetValueFromString(const nsAString& aString,
103 : float& aValue,
104 : uint16_t* aUnitType)
105 : {
106 : RangedPtr<const char16_t> iter =
107 106 : SVGContentUtils::GetStartRangedPtr(aString);
108 : const RangedPtr<const char16_t> end =
109 106 : SVGContentUtils::GetEndRangedPtr(aString);
110 :
111 106 : if (!SVGContentUtils::ParseNumber(iter, end, aValue)) {
112 0 : return false;
113 : }
114 212 : const nsAString& units = Substring(iter.get(), end.get());
115 106 : *aUnitType = GetUnitTypeForString(units);
116 106 : return IsValidUnitType(*aUnitType);
117 : }
118 :
119 0 : static float GetMMPerPixel() { return MM_PER_INCH_FLOAT / 96; }
120 :
121 : static float
122 0 : FixAxisLength(float aLength)
123 : {
124 0 : if (aLength == 0.0f) {
125 0 : LAYOUT_WARNING("zero axis length");
126 0 : return 1e-20f;
127 : }
128 0 : return aLength;
129 : }
130 :
131 1489 : SVGElementMetrics::SVGElementMetrics(nsSVGElement* aSVGElement,
132 1489 : SVGSVGElement* aCtx)
133 : : mSVGElement(aSVGElement)
134 1489 : , mCtx(aCtx)
135 : {
136 1489 : }
137 :
138 : float
139 0 : SVGElementMetrics::GetEmLength() const
140 : {
141 0 : return SVGContentUtils::GetFontSize(mSVGElement);
142 : }
143 :
144 : float
145 0 : SVGElementMetrics::GetExLength() const
146 : {
147 0 : return SVGContentUtils::GetFontXHeight(mSVGElement);
148 : }
149 :
150 : float
151 0 : SVGElementMetrics::GetAxisLength(uint8_t aCtxType) const
152 : {
153 0 : if (!EnsureCtx()) {
154 0 : return 1;
155 : }
156 :
157 0 : return FixAxisLength(mCtx->GetLength(aCtxType));
158 : }
159 :
160 : bool
161 0 : SVGElementMetrics::EnsureCtx() const
162 : {
163 0 : if (!mCtx && mSVGElement) {
164 0 : mCtx = mSVGElement->GetCtx();
165 0 : if (!mCtx && mSVGElement->IsSVGElement(nsGkAtoms::svg)) {
166 : // mSVGElement must be the outer svg element
167 0 : mCtx = static_cast<SVGSVGElement*>(mSVGElement);
168 : }
169 : }
170 0 : return mCtx != nullptr;
171 : }
172 :
173 0 : NonSVGFrameUserSpaceMetrics::NonSVGFrameUserSpaceMetrics(nsIFrame* aFrame)
174 0 : : mFrame(aFrame)
175 : {
176 0 : }
177 :
178 : float
179 0 : NonSVGFrameUserSpaceMetrics::GetEmLength() const
180 : {
181 0 : return SVGContentUtils::GetFontSize(mFrame);
182 : }
183 :
184 : float
185 0 : NonSVGFrameUserSpaceMetrics::GetExLength() const
186 : {
187 0 : return SVGContentUtils::GetFontXHeight(mFrame);
188 : }
189 :
190 : gfx::Size
191 0 : NonSVGFrameUserSpaceMetrics::GetSize() const
192 : {
193 0 : return nsSVGIntegrationUtils::GetSVGCoordContextForNonSVGFrame(mFrame);
194 : }
195 :
196 : float
197 0 : UserSpaceMetricsWithSize::GetAxisLength(uint8_t aCtxType) const
198 : {
199 0 : gfx::Size size = GetSize();
200 : float length;
201 0 : switch (aCtxType) {
202 : case SVGContentUtils::X:
203 0 : length = size.width;
204 0 : break;
205 : case SVGContentUtils::Y:
206 0 : length = size.height;
207 0 : break;
208 : case SVGContentUtils::XY:
209 0 : length = SVGContentUtils::ComputeNormalizedHypotenuse(size.width, size.height);
210 0 : break;
211 : default:
212 0 : NS_NOTREACHED("Unknown axis type");
213 0 : length = 1;
214 0 : break;
215 : }
216 0 : return FixAxisLength(length);
217 : }
218 :
219 : float
220 236 : nsSVGLength2::GetUnitScaleFactor(nsSVGElement *aSVGElement,
221 : uint8_t aUnitType) const
222 : {
223 236 : return GetUnitScaleFactor(SVGElementMetrics(aSVGElement), aUnitType);
224 : }
225 :
226 : float
227 1253 : nsSVGLength2::GetUnitScaleFactor(SVGSVGElement *aCtx, uint8_t aUnitType) const
228 : {
229 1253 : return GetUnitScaleFactor(SVGElementMetrics(aCtx, aCtx), aUnitType);
230 : }
231 :
232 : float
233 0 : nsSVGLength2::GetUnitScaleFactor(nsIFrame *aFrame, uint8_t aUnitType) const
234 : {
235 0 : nsIContent* content = aFrame->GetContent();
236 0 : if (content->IsSVGElement()) {
237 0 : return GetUnitScaleFactor(SVGElementMetrics(static_cast<nsSVGElement*>(content)), aUnitType);
238 : }
239 0 : return GetUnitScaleFactor(NonSVGFrameUserSpaceMetrics(aFrame), aUnitType);
240 : }
241 :
242 : float
243 1489 : nsSVGLength2::GetUnitScaleFactor(const UserSpaceMetrics& aMetrics, uint8_t aUnitType) const
244 : {
245 1489 : switch (aUnitType) {
246 : case nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER:
247 : case nsIDOMSVGLength::SVG_LENGTHTYPE_PX:
248 1489 : return 1;
249 : case nsIDOMSVGLength::SVG_LENGTHTYPE_MM:
250 0 : return GetMMPerPixel();
251 : case nsIDOMSVGLength::SVG_LENGTHTYPE_CM:
252 0 : return GetMMPerPixel() / 10.0f;
253 : case nsIDOMSVGLength::SVG_LENGTHTYPE_IN:
254 0 : return GetMMPerPixel() / MM_PER_INCH_FLOAT;
255 : case nsIDOMSVGLength::SVG_LENGTHTYPE_PT:
256 0 : return GetMMPerPixel() * POINTS_PER_INCH_FLOAT / MM_PER_INCH_FLOAT;
257 : case nsIDOMSVGLength::SVG_LENGTHTYPE_PC:
258 0 : return GetMMPerPixel() * POINTS_PER_INCH_FLOAT / MM_PER_INCH_FLOAT / 12.0f;
259 : case nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE:
260 0 : return 100.0f / aMetrics.GetAxisLength(mCtxType);
261 : case nsIDOMSVGLength::SVG_LENGTHTYPE_EMS:
262 0 : return 1 / aMetrics.GetEmLength();
263 : case nsIDOMSVGLength::SVG_LENGTHTYPE_EXS:
264 0 : return 1 / aMetrics.GetExLength();
265 : default:
266 0 : NS_NOTREACHED("Unknown unit type");
267 0 : return 0;
268 : }
269 : }
270 :
271 : void
272 0 : nsSVGLength2::SetBaseValueInSpecifiedUnits(float aValue,
273 : nsSVGElement *aSVGElement,
274 : bool aDoSetAttr)
275 : {
276 0 : if (mIsBaseSet && mBaseVal == aValue) {
277 0 : return;
278 : }
279 :
280 0 : nsAttrValue emptyOrOldValue;
281 0 : if (aDoSetAttr) {
282 0 : emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum);
283 : }
284 0 : mBaseVal = aValue;
285 0 : mIsBaseSet = true;
286 0 : if (!mIsAnimated) {
287 0 : mAnimVal = mBaseVal;
288 : }
289 : else {
290 0 : aSVGElement->AnimationNeedsResample();
291 : }
292 0 : if (aDoSetAttr) {
293 0 : aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
294 : }
295 : }
296 :
297 : nsresult
298 0 : nsSVGLength2::ConvertToSpecifiedUnits(uint16_t unitType,
299 : nsSVGElement *aSVGElement)
300 : {
301 0 : if (!IsValidUnitType(unitType))
302 0 : return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
303 :
304 0 : if (mIsBaseSet && mSpecifiedUnitType == uint8_t(unitType))
305 0 : return NS_OK;
306 :
307 : // Even though we're not changing the visual effect this length will have
308 : // on the document, we still need to send out notifications in case we have
309 : // mutation listeners, since the actual string value of the attribute will
310 : // change.
311 0 : nsAttrValue emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum);
312 :
313 : float valueInUserUnits =
314 0 : mBaseVal / GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType);
315 0 : mSpecifiedUnitType = uint8_t(unitType);
316 : // Setting aDoSetAttr to false here will ensure we don't call
317 : // Will/DidChangeAngle a second time (and dispatch duplicate notifications).
318 0 : SetBaseValue(valueInUserUnits, aSVGElement, false);
319 :
320 0 : aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
321 :
322 0 : return NS_OK;
323 : }
324 :
325 : nsresult
326 0 : nsSVGLength2::NewValueSpecifiedUnits(uint16_t unitType,
327 : float valueInSpecifiedUnits,
328 : nsSVGElement *aSVGElement)
329 : {
330 0 : NS_ENSURE_FINITE(valueInSpecifiedUnits, NS_ERROR_ILLEGAL_VALUE);
331 :
332 0 : if (!IsValidUnitType(unitType))
333 0 : return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
334 :
335 0 : if (mIsBaseSet && mBaseVal == valueInSpecifiedUnits &&
336 0 : mSpecifiedUnitType == uint8_t(unitType)) {
337 0 : return NS_OK;
338 : }
339 :
340 0 : nsAttrValue emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum);
341 0 : mBaseVal = valueInSpecifiedUnits;
342 0 : mIsBaseSet = true;
343 0 : mSpecifiedUnitType = uint8_t(unitType);
344 0 : if (!mIsAnimated) {
345 0 : mAnimVal = mBaseVal;
346 : }
347 : else {
348 0 : aSVGElement->AnimationNeedsResample();
349 : }
350 0 : aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
351 0 : return NS_OK;
352 : }
353 :
354 : nsresult
355 0 : nsSVGLength2::ToDOMBaseVal(DOMSVGLength **aResult, nsSVGElement *aSVGElement)
356 : {
357 : RefPtr<DOMSVGLength> domBaseVal =
358 0 : DOMSVGLength::GetTearOff(this, aSVGElement, false);
359 :
360 0 : domBaseVal.forget(aResult);
361 0 : return NS_OK;
362 : }
363 :
364 : nsresult
365 0 : nsSVGLength2::ToDOMAnimVal(DOMSVGLength **aResult, nsSVGElement *aSVGElement)
366 : {
367 : RefPtr<DOMSVGLength> domAnimVal =
368 0 : DOMSVGLength::GetTearOff(this, aSVGElement, true);
369 :
370 0 : domAnimVal.forget(aResult);
371 0 : return NS_OK;
372 : }
373 :
374 : /* Implementation */
375 :
376 : nsresult
377 106 : nsSVGLength2::SetBaseValueString(const nsAString &aValueAsString,
378 : nsSVGElement *aSVGElement,
379 : bool aDoSetAttr)
380 : {
381 : float value;
382 : uint16_t unitType;
383 :
384 106 : if (!GetValueFromString(aValueAsString, value, &unitType)) {
385 0 : return NS_ERROR_DOM_SYNTAX_ERR;
386 : }
387 :
388 106 : if (mIsBaseSet && mBaseVal == float(value) &&
389 0 : mSpecifiedUnitType == uint8_t(unitType)) {
390 0 : return NS_OK;
391 : }
392 :
393 212 : nsAttrValue emptyOrOldValue;
394 106 : if (aDoSetAttr) {
395 0 : emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum);
396 : }
397 106 : mBaseVal = value;
398 106 : mIsBaseSet = true;
399 106 : mSpecifiedUnitType = uint8_t(unitType);
400 106 : if (!mIsAnimated) {
401 106 : mAnimVal = mBaseVal;
402 : }
403 : else {
404 0 : aSVGElement->AnimationNeedsResample();
405 : }
406 :
407 106 : if (aDoSetAttr) {
408 0 : aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
409 : }
410 106 : return NS_OK;
411 : }
412 :
413 : void
414 0 : nsSVGLength2::GetBaseValueString(nsAString & aValueAsString) const
415 : {
416 0 : GetValueString(aValueAsString, mBaseVal, mSpecifiedUnitType);
417 0 : }
418 :
419 : void
420 0 : nsSVGLength2::GetAnimValueString(nsAString & aValueAsString) const
421 : {
422 0 : GetValueString(aValueAsString, mAnimVal, mSpecifiedUnitType);
423 0 : }
424 :
425 : void
426 0 : nsSVGLength2::SetBaseValue(float aValue, nsSVGElement *aSVGElement,
427 : bool aDoSetAttr)
428 : {
429 0 : SetBaseValueInSpecifiedUnits(aValue * GetUnitScaleFactor(aSVGElement,
430 0 : mSpecifiedUnitType),
431 0 : aSVGElement, aDoSetAttr);
432 0 : }
433 :
434 : void
435 0 : nsSVGLength2::SetAnimValueInSpecifiedUnits(float aValue,
436 : nsSVGElement* aSVGElement)
437 : {
438 0 : if (mAnimVal == aValue && mIsAnimated) {
439 0 : return;
440 : }
441 0 : mAnimVal = aValue;
442 0 : mIsAnimated = true;
443 0 : aSVGElement->DidAnimateLength(mAttrEnum);
444 : }
445 :
446 : void
447 0 : nsSVGLength2::SetAnimValue(float aValue, nsSVGElement *aSVGElement)
448 : {
449 0 : SetAnimValueInSpecifiedUnits(aValue * GetUnitScaleFactor(aSVGElement,
450 0 : mSpecifiedUnitType),
451 0 : aSVGElement);
452 0 : }
453 :
454 : already_AddRefed<SVGAnimatedLength>
455 0 : nsSVGLength2::ToDOMAnimatedLength(nsSVGElement* aSVGElement)
456 : {
457 : RefPtr<SVGAnimatedLength> svgAnimatedLength =
458 0 : sSVGAnimatedLengthTearoffTable.GetTearoff(this);
459 0 : if (!svgAnimatedLength) {
460 0 : svgAnimatedLength = new SVGAnimatedLength(this, aSVGElement);
461 0 : sSVGAnimatedLengthTearoffTable.AddTearoff(this, svgAnimatedLength);
462 : }
463 :
464 0 : return svgAnimatedLength.forget();
465 : }
466 :
467 0 : SVGAnimatedLength::~SVGAnimatedLength()
468 : {
469 0 : sSVGAnimatedLengthTearoffTable.RemoveTearoff(mVal);
470 0 : }
471 :
472 : UniquePtr<nsISMILAttr>
473 0 : nsSVGLength2::ToSMILAttr(nsSVGElement *aSVGElement)
474 : {
475 0 : return MakeUnique<SMILLength>(this, aSVGElement);
476 : }
477 :
478 : nsresult
479 0 : nsSVGLength2::SMILLength::ValueFromString(const nsAString& aStr,
480 : const SVGAnimationElement* /*aSrcElement*/,
481 : nsSMILValue& aValue,
482 : bool& aPreventCachingOfSandwich) const
483 : {
484 : float value;
485 : uint16_t unitType;
486 :
487 0 : if (!GetValueFromString(aStr, value, &unitType)) {
488 0 : return NS_ERROR_DOM_SYNTAX_ERR;
489 : }
490 :
491 0 : nsSMILValue val(nsSMILFloatType::Singleton());
492 0 : val.mU.mDouble = value / mVal->GetUnitScaleFactor(mSVGElement, unitType);
493 0 : aValue = val;
494 0 : aPreventCachingOfSandwich =
495 0 : (unitType == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE ||
496 0 : unitType == nsIDOMSVGLength::SVG_LENGTHTYPE_EMS ||
497 0 : unitType == nsIDOMSVGLength::SVG_LENGTHTYPE_EXS);
498 :
499 0 : return NS_OK;
500 : }
501 :
502 : nsSMILValue
503 0 : nsSVGLength2::SMILLength::GetBaseValue() const
504 : {
505 0 : nsSMILValue val(nsSMILFloatType::Singleton());
506 0 : val.mU.mDouble = mVal->GetBaseValue(mSVGElement);
507 0 : return val;
508 : }
509 :
510 : void
511 0 : nsSVGLength2::SMILLength::ClearAnimValue()
512 : {
513 0 : if (mVal->mIsAnimated) {
514 0 : mVal->mIsAnimated = false;
515 0 : mVal->mAnimVal = mVal->mBaseVal;
516 0 : mSVGElement->DidAnimateLength(mVal->mAttrEnum);
517 : }
518 0 : }
519 :
520 : nsresult
521 0 : nsSVGLength2::SMILLength::SetAnimValue(const nsSMILValue& aValue)
522 : {
523 0 : NS_ASSERTION(aValue.mType == nsSMILFloatType::Singleton(),
524 : "Unexpected type to assign animated value");
525 0 : if (aValue.mType == nsSMILFloatType::Singleton()) {
526 0 : mVal->SetAnimValue(float(aValue.mU.mDouble), mSVGElement);
527 : }
528 0 : return NS_OK;
529 : }
|