Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/svg/DOMSVGLength.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 "DOMSVGLength.h"
8
#include "DOMSVGLengthList.h"
9
#include "DOMSVGAnimatedLengthList.h"
10
#include "SVGLength.h"
11
#include "SVGAnimatedLengthList.h"
12
#include "nsSVGElement.h"
13
#include "nsSVGLength2.h"
14
#include "nsError.h"
15
#include "nsMathUtils.h"
16
#include "mozilla/dom/SVGLengthBinding.h"
17
#include "mozilla/FloatingPoint.h"
18
#include "nsSVGAttrTearoffTable.h"
19
20
// See the architecture comment in DOMSVGAnimatedLengthList.h.
21
22
namespace mozilla {
23
24
using namespace dom;
25
26
static nsSVGAttrTearoffTable<nsSVGLength2, DOMSVGLength>
27
  sBaseSVGLengthTearOffTable,
28
  sAnimSVGLengthTearOffTable;
29
30
// We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to
31
// clear our list's weak ref to us to be safe. (The other option would be to
32
// not unlink and rely on the breaking of the other edges in the cycle, as
33
// NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
34
NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGLength)
35
36
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGLength)
37
0
  tmp->CleanupWeakRefs();
38
0
  tmp->mVal = nullptr; // (owned by mSVGElement, which we drop our ref to here)
39
0
NS_IMPL_CYCLE_COLLECTION_UNLINK(mList)
40
0
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSVGElement)
41
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
42
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
43
44
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGLength)
45
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mList)
46
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSVGElement)
47
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
48
49
0
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGLength)
50
0
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
51
0
NS_IMPL_CYCLE_COLLECTION_TRACE_END
52
53
NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGLength)
54
NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGLength)
55
56
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGLength)
57
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
58
0
  NS_INTERFACE_MAP_ENTRY(mozilla::DOMSVGLength) // pseudo-interface
59
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
60
0
NS_INTERFACE_MAP_END
61
62
//----------------------------------------------------------------------
63
// Helper class: AutoChangeLengthNotifier
64
// Stack-based helper class to pair calls to WillChangeLengthList and
65
// DidChangeLengthList.
66
class MOZ_RAII AutoChangeLengthNotifier
67
{
68
public:
69
  explicit AutoChangeLengthNotifier(DOMSVGLength* aLength MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
70
    : mLength(aLength)
71
0
  {
72
0
    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
73
0
    MOZ_ASSERT(mLength, "Expecting non-null length");
74
0
    MOZ_ASSERT(mLength->HasOwner(),
75
0
               "Expecting list to have an owner for notification");
76
0
    mEmptyOrOldValue =
77
0
      mLength->Element()->WillChangeLengthList(mLength->mAttrEnum);
78
0
  }
79
80
  ~AutoChangeLengthNotifier()
81
0
  {
82
0
    mLength->Element()->DidChangeLengthList(mLength->mAttrEnum,
83
0
                                            mEmptyOrOldValue);
84
0
    if (mLength->mList->IsAnimating()) {
85
0
      mLength->Element()->AnimationNeedsResample();
86
0
    }
87
0
  }
88
89
private:
90
  DOMSVGLength* const mLength;
91
  nsAttrValue   mEmptyOrOldValue;
92
  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
93
};
94
95
DOMSVGLength::DOMSVGLength(DOMSVGLengthList *aList,
96
                           uint8_t aAttrEnum,
97
                           uint32_t aListIndex,
98
                           bool aIsAnimValItem)
99
  : mList(aList)
100
  , mListIndex(aListIndex)
101
  , mAttrEnum(aAttrEnum)
102
  , mIsAnimValItem(aIsAnimValItem)
103
  , mUnit(SVGLength_Binding::SVG_LENGTHTYPE_NUMBER)
104
  , mValue(0.0f)
105
  , mVal(nullptr)
106
0
{
107
0
  // These shifts are in sync with the members in the header.
108
0
  MOZ_ASSERT(aList &&
109
0
             aAttrEnum < (1 << 4) &&
110
0
             aListIndex <= MaxListIndex(),
111
0
             "bad arg");
112
0
113
0
  MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGNumber!");
114
0
}
115
116
DOMSVGLength::DOMSVGLength()
117
  : mList(nullptr)
118
  , mListIndex(0)
119
  , mAttrEnum(0)
120
  , mIsAnimValItem(false)
121
  , mUnit(SVGLength_Binding::SVG_LENGTHTYPE_NUMBER)
122
  , mValue(0.0f)
123
  , mVal(nullptr)
124
0
{
125
0
}
126
127
DOMSVGLength::DOMSVGLength(nsSVGLength2* aVal, nsSVGElement* aSVGElement,
128
                           bool aAnimVal)
129
  : mList(nullptr)
130
  , mListIndex(0)
131
  , mAttrEnum(0)
132
  , mIsAnimValItem(aAnimVal)
133
  , mUnit(SVGLength_Binding::SVG_LENGTHTYPE_NUMBER)
134
  , mValue(0.0f)
135
  , mVal(aVal)
136
  , mSVGElement(aSVGElement)
137
0
{
138
0
}
139
140
void
141
DOMSVGLength::CleanupWeakRefs()
142
0
{
143
0
  // Our mList's weak ref to us must be nulled out when we die (or when we're
144
0
  // cycle collected), so we that don't leave behind a pointer to
145
0
  // free / soon-to-be-free memory.
146
0
  if (mList) {
147
0
    MOZ_ASSERT(mList->mItems[mListIndex] == this,
148
0
               "Clearing out the wrong list index...?");
149
0
    mList->mItems[mListIndex] = nullptr;
150
0
  }
151
0
152
0
  // Similarly, we must update the tearoff table to remove its (non-owning)
153
0
  // pointer to mVal.
154
0
  if (mVal) {
155
0
    auto& table = mIsAnimValItem ?
156
0
      sAnimSVGLengthTearOffTable : sBaseSVGLengthTearOffTable;
157
0
    table.RemoveTearoff(mVal);
158
0
  }
159
0
}
160
161
DOMSVGLength::~DOMSVGLength()
162
0
{
163
0
  CleanupWeakRefs();
164
0
}
165
166
already_AddRefed<DOMSVGLength>
167
DOMSVGLength::GetTearOff(nsSVGLength2* aVal, nsSVGElement* aSVGElement,
168
                         bool aAnimVal)
169
0
{
170
0
  auto& table = aAnimVal ? sAnimSVGLengthTearOffTable : sBaseSVGLengthTearOffTable;
171
0
  RefPtr<DOMSVGLength> domLength = table.GetTearoff(aVal);
172
0
  if (!domLength) {
173
0
    domLength = new DOMSVGLength(aVal, aSVGElement, aAnimVal);
174
0
    table.AddTearoff(aVal, domLength);
175
0
  }
176
0
177
0
  return domLength.forget();
178
0
}
179
180
DOMSVGLength*
181
DOMSVGLength::Copy()
182
0
{
183
0
  NS_ASSERTION(HasOwner() || IsReflectingAttribute(), "unexpected caller");
184
0
  DOMSVGLength *copy = new DOMSVGLength();
185
0
  uint16_t unit;
186
0
  float value;
187
0
  if (mVal) {
188
0
    unit = mVal->mSpecifiedUnitType;
189
0
    value = mIsAnimValItem ? mVal->mAnimVal : mVal->mBaseVal;
190
0
  } else {
191
0
    SVGLength &length = InternalItem();
192
0
    unit = length.GetUnit();
193
0
    value = length.GetValueInCurrentUnits();
194
0
  }
195
0
  copy->NewValueSpecifiedUnits(unit, value, IgnoreErrors());
196
0
  return copy;
197
0
}
198
199
uint16_t
200
DOMSVGLength::UnitType()
201
0
{
202
0
  if (mVal) {
203
0
    if (mIsAnimValItem) {
204
0
      mSVGElement->FlushAnimations();
205
0
    }
206
0
    return mVal->mSpecifiedUnitType;
207
0
  }
208
0
209
0
  if (mIsAnimValItem && HasOwner()) {
210
0
    Element()->FlushAnimations(); // May make HasOwner() == false
211
0
  }
212
0
  return HasOwner() ? InternalItem().GetUnit() : mUnit;
213
0
}
214
215
float
216
DOMSVGLength::GetValue(ErrorResult& aRv)
217
0
{
218
0
  if (mVal) {
219
0
    if (mIsAnimValItem) {
220
0
      mSVGElement->FlushAnimations();
221
0
      return mVal->GetAnimValue(mSVGElement);
222
0
    }
223
0
    return mVal->GetBaseValue(mSVGElement);
224
0
  }
225
0
226
0
  if (mIsAnimValItem && HasOwner()) {
227
0
    Element()->FlushAnimations(); // May make HasOwner() == false
228
0
  }
229
0
  if (HasOwner()) {
230
0
    float value = InternalItem().GetValueInUserUnits(Element(), Axis());
231
0
    if (!IsFinite(value)) {
232
0
      aRv.Throw(NS_ERROR_FAILURE);
233
0
    }
234
0
    return value;
235
0
  } else if (mUnit == SVGLength_Binding::SVG_LENGTHTYPE_NUMBER ||
236
0
             mUnit == SVGLength_Binding::SVG_LENGTHTYPE_PX) {
237
0
    return mValue;
238
0
  }
239
0
  // else [SVGWG issue] Can't convert this length's value to user units
240
0
  // ReportToConsole
241
0
  aRv.Throw(NS_ERROR_FAILURE);
242
0
  return 0.0f;
243
0
}
244
245
void
246
DOMSVGLength::SetValue(float aUserUnitValue, ErrorResult& aRv)
247
0
{
248
0
  if (mIsAnimValItem) {
249
0
    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
250
0
    return;
251
0
  }
252
0
253
0
  if (mVal) {
254
0
    aRv = mVal->SetBaseValue(aUserUnitValue, mSVGElement, true);
255
0
    return;
256
0
  }
257
0
258
0
  // Although the value passed in is in user units, this method does not turn
259
0
  // this length into a user unit length. Instead it converts the user unit
260
0
  // value to this length's current unit and sets that, leaving this length's
261
0
  // unit as it is.
262
0
263
0
  if (HasOwner()) {
264
0
    if (InternalItem().GetValueInUserUnits(Element(), Axis()) ==
265
0
        aUserUnitValue) {
266
0
      return;
267
0
    }
268
0
    float uuPerUnit = InternalItem().GetUserUnitsPerUnit(Element(), Axis());
269
0
    if (uuPerUnit > 0) {
270
0
      float newValue = aUserUnitValue / uuPerUnit;
271
0
      if (IsFinite(newValue)) {
272
0
        AutoChangeLengthNotifier notifier(this);
273
0
        InternalItem().SetValueAndUnit(newValue, InternalItem().GetUnit());
274
0
        return;
275
0
      }
276
0
    }
277
0
  } else if (mUnit == SVGLength_Binding::SVG_LENGTHTYPE_NUMBER ||
278
0
             mUnit == SVGLength_Binding::SVG_LENGTHTYPE_PX) {
279
0
    mValue = aUserUnitValue;
280
0
    return;
281
0
  }
282
0
  // else [SVGWG issue] Can't convert user unit value to this length's unit
283
0
  // ReportToConsole
284
0
  aRv.Throw(NS_ERROR_FAILURE);
285
0
}
286
287
float
288
DOMSVGLength::ValueInSpecifiedUnits()
289
0
{
290
0
  if (mVal) {
291
0
    if (mIsAnimValItem) {
292
0
      mSVGElement->FlushAnimations();
293
0
      return mVal->mAnimVal;
294
0
    }
295
0
    return mVal->mBaseVal;
296
0
  }
297
0
298
0
  if (mIsAnimValItem && HasOwner()) {
299
0
    Element()->FlushAnimations(); // May make HasOwner() == false
300
0
  }
301
0
  return HasOwner() ? InternalItem().GetValueInCurrentUnits() : mValue;
302
0
}
303
304
void
305
DOMSVGLength::SetValueInSpecifiedUnits(float aValue, ErrorResult& aRv)
306
0
{
307
0
  if (mIsAnimValItem) {
308
0
    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
309
0
    return;
310
0
  }
311
0
312
0
  if (mVal) {
313
0
    mVal->SetBaseValueInSpecifiedUnits(aValue, mSVGElement, true);
314
0
    return;
315
0
  }
316
0
317
0
  if (HasOwner()) {
318
0
    if (InternalItem().GetValueInCurrentUnits() == aValue) {
319
0
      return;
320
0
    }
321
0
    AutoChangeLengthNotifier notifier(this);
322
0
    InternalItem().SetValueInCurrentUnits(aValue);
323
0
    return;
324
0
  }
325
0
  mValue = aValue;
326
0
}
327
328
void
329
DOMSVGLength::SetValueAsString(const nsAString& aValue, ErrorResult& aRv)
330
0
{
331
0
  if (mIsAnimValItem) {
332
0
    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
333
0
    return;
334
0
  }
335
0
336
0
  if (mVal) {
337
0
    aRv = mVal->SetBaseValueString(aValue, mSVGElement, true);
338
0
    return;
339
0
  }
340
0
341
0
  SVGLength value;
342
0
  if (!value.SetValueFromString(aValue)) {
343
0
    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
344
0
    return;
345
0
  }
346
0
  if (HasOwner()) {
347
0
    if (InternalItem() == value) {
348
0
      return;
349
0
    }
350
0
    AutoChangeLengthNotifier notifier(this);
351
0
    InternalItem() = value;
352
0
    return;
353
0
  }
354
0
  mValue = value.GetValueInCurrentUnits();
355
0
  mUnit = value.GetUnit();
356
0
}
357
358
void
359
DOMSVGLength::GetValueAsString(nsAString& aValue)
360
0
{
361
0
  if (mVal) {
362
0
    if (mIsAnimValItem) {
363
0
      mSVGElement->FlushAnimations();
364
0
      mVal->GetAnimValueString(aValue);
365
0
    } else {
366
0
      mVal->GetBaseValueString(aValue);
367
0
    }
368
0
    return;
369
0
  }
370
0
371
0
  if (mIsAnimValItem && HasOwner()) {
372
0
    Element()->FlushAnimations(); // May make HasOwner() == false
373
0
  }
374
0
  if (HasOwner()) {
375
0
    InternalItem().GetValueAsString(aValue);
376
0
    return;
377
0
  }
378
0
  SVGLength(mValue, mUnit).GetValueAsString(aValue);
379
0
}
380
381
void
382
DOMSVGLength::NewValueSpecifiedUnits(uint16_t aUnit, float aValue,
383
                                     ErrorResult& aRv)
384
0
{
385
0
  if (mIsAnimValItem) {
386
0
    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
387
0
    return;
388
0
  }
389
0
390
0
  if (mVal) {
391
0
    mVal->NewValueSpecifiedUnits(aUnit, aValue, mSVGElement);
392
0
    return;
393
0
  }
394
0
395
0
  if (!SVGLength::IsValidUnitType(aUnit)) {
396
0
    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
397
0
    return;
398
0
  }
399
0
  if (HasOwner()) {
400
0
    if (InternalItem().GetUnit() == aUnit &&
401
0
        InternalItem().GetValueInCurrentUnits() == aValue) {
402
0
      return;
403
0
    }
404
0
    AutoChangeLengthNotifier notifier(this);
405
0
    InternalItem().SetValueAndUnit(aValue, uint8_t(aUnit));
406
0
    return;
407
0
  }
408
0
  mUnit = uint8_t(aUnit);
409
0
  mValue = aValue;
410
0
}
411
412
void
413
DOMSVGLength::ConvertToSpecifiedUnits(uint16_t aUnit, ErrorResult& aRv)
414
0
{
415
0
  if (mIsAnimValItem) {
416
0
    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
417
0
    return;
418
0
  }
419
0
420
0
  if (mVal) {
421
0
    mVal->ConvertToSpecifiedUnits(aUnit, mSVGElement);
422
0
    return;
423
0
  }
424
0
425
0
  if (!SVGLength::IsValidUnitType(aUnit)) {
426
0
    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
427
0
    return;
428
0
  }
429
0
  if (HasOwner()) {
430
0
    if (InternalItem().GetUnit() == aUnit) {
431
0
      return;
432
0
    }
433
0
    float val = InternalItem().GetValueInSpecifiedUnit(
434
0
                                 aUnit, Element(), Axis());
435
0
    if (IsFinite(val)) {
436
0
      AutoChangeLengthNotifier notifier(this);
437
0
      InternalItem().SetValueAndUnit(val, aUnit);
438
0
      return;
439
0
    }
440
0
  } else {
441
0
    SVGLength len(mValue, mUnit);
442
0
    float val = len.GetValueInSpecifiedUnit(aUnit, nullptr, 0);
443
0
    if (IsFinite(val)) {
444
0
      mValue = val;
445
0
      mUnit = aUnit;
446
0
      return;
447
0
    }
448
0
  }
449
0
  // else [SVGWG issue] Can't convert unit
450
0
  // ReportToConsole
451
0
  aRv.Throw(NS_ERROR_FAILURE);
452
0
}
453
454
JSObject*
455
DOMSVGLength::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
456
0
{
457
0
  return SVGLength_Binding::Wrap(aCx, this, aGivenProto);
458
0
}
459
460
void
461
DOMSVGLength::InsertingIntoList(DOMSVGLengthList *aList,
462
                                uint8_t aAttrEnum,
463
                                uint32_t aListIndex,
464
                                bool aIsAnimValItem)
465
0
{
466
0
  NS_ASSERTION(!HasOwner(), "Inserting item that is already in a list");
467
0
468
0
  mList = aList;
469
0
  mAttrEnum = aAttrEnum;
470
0
  mListIndex = aListIndex;
471
0
  mIsAnimValItem = aIsAnimValItem;
472
0
473
0
  MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGLength!");
474
0
}
475
476
void
477
DOMSVGLength::RemovingFromList()
478
0
{
479
0
  mValue = InternalItem().GetValueInCurrentUnits();
480
0
  mUnit  = InternalItem().GetUnit();
481
0
  mList = nullptr;
482
0
  mIsAnimValItem = false;
483
0
}
484
485
SVGLength
486
DOMSVGLength::ToSVGLength()
487
0
{
488
0
  if (HasOwner()) {
489
0
    return SVGLength(InternalItem().GetValueInCurrentUnits(),
490
0
                     InternalItem().GetUnit());
491
0
  }
492
0
  return SVGLength(mValue, mUnit);
493
0
}
494
495
SVGLength&
496
DOMSVGLength::InternalItem()
497
0
{
498
0
  SVGAnimatedLengthList *alist = Element()->GetAnimatedLengthList(mAttrEnum);
499
0
  return mIsAnimValItem && alist->mAnimVal ?
500
0
    (*alist->mAnimVal)[mListIndex] :
501
0
    alist->mBaseVal[mListIndex];
502
0
}
503
504
#ifdef DEBUG
505
bool
506
DOMSVGLength::IndexIsValid()
507
{
508
  SVGAnimatedLengthList *alist = Element()->GetAnimatedLengthList(mAttrEnum);
509
  return (mIsAnimValItem &&
510
          mListIndex < alist->GetAnimValue().Length()) ||
511
         (!mIsAnimValItem &&
512
          mListIndex < alist->GetBaseValue().Length());
513
}
514
#endif
515
516
} // namespace mozilla