Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/svg/nsSVGViewBox.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 "nsSVGViewBox.h"
8
9
#include "mozilla/Move.h"
10
#include "nsCharSeparatedTokenizer.h"
11
#include "nsSMILValue.h"
12
#include "nsTextFormatter.h"
13
#include "SVGContentUtils.h"
14
#include "SVGViewBoxSMILType.h"
15
16
0
#define NUM_VIEWBOX_COMPONENTS 4
17
using namespace mozilla;
18
19
/* Implementation of nsSVGViewBoxRect methods */
20
21
bool
22
nsSVGViewBoxRect::operator==(const nsSVGViewBoxRect& aOther) const
23
0
{
24
0
  if (&aOther == this)
25
0
    return true;
26
0
27
0
  return (none && aOther.none) ||
28
0
    (!none && !aOther.none &&
29
0
     x == aOther.x &&
30
0
     y == aOther.y &&
31
0
     width == aOther.width &&
32
0
     height == aOther.height);
33
0
}
34
35
/* static */ nsresult
36
nsSVGViewBoxRect::FromString(const nsAString& aStr, nsSVGViewBoxRect *aViewBox)
37
0
{
38
0
  if (aStr.EqualsLiteral("none")) {
39
0
    aViewBox->none = true;
40
0
    return NS_OK;
41
0
  }
42
0
43
0
  nsCharSeparatedTokenizerTemplate<nsContentUtils::IsHTMLWhitespace>
44
0
    tokenizer(aStr, ',', nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
45
0
  float vals[NUM_VIEWBOX_COMPONENTS];
46
0
  uint32_t i;
47
0
  for (i = 0; i < NUM_VIEWBOX_COMPONENTS && tokenizer.hasMoreTokens(); ++i) {
48
0
    if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), vals[i])) {
49
0
      return NS_ERROR_DOM_SYNTAX_ERR;
50
0
    }
51
0
  }
52
0
53
0
  if (i != NUM_VIEWBOX_COMPONENTS ||              // Too few values.
54
0
      tokenizer.hasMoreTokens() ||                // Too many values.
55
0
      tokenizer.separatorAfterCurrentToken()) {   // Trailing comma.
56
0
    return NS_ERROR_DOM_SYNTAX_ERR;
57
0
  }
58
0
59
0
  aViewBox->x = vals[0];
60
0
  aViewBox->y = vals[1];
61
0
  aViewBox->width = vals[2];
62
0
  aViewBox->height = vals[3];
63
0
  aViewBox->none = false;
64
0
65
0
  return NS_OK;
66
0
}
67
68
69
/* Cycle collection macros for nsSVGViewBox */
70
71
NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(nsSVGViewBox::DOMBaseVal, mSVGElement)
72
NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(nsSVGViewBox::DOMAnimVal, mSVGElement)
73
74
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGViewBox::DOMBaseVal)
75
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGViewBox::DOMBaseVal)
76
77
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGViewBox::DOMAnimVal)
78
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGViewBox::DOMAnimVal)
79
80
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGViewBox::DOMBaseVal)
81
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
82
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
83
0
NS_INTERFACE_MAP_END
84
85
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGViewBox::DOMAnimVal)
86
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
87
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
88
0
NS_INTERFACE_MAP_END
89
90
static nsSVGAttrTearoffTable<nsSVGViewBox, nsSVGViewBox::DOMBaseVal>
91
  sBaseSVGViewBoxTearoffTable;
92
static nsSVGAttrTearoffTable<nsSVGViewBox, nsSVGViewBox::DOMAnimVal>
93
  sAnimSVGViewBoxTearoffTable;
94
nsSVGAttrTearoffTable<nsSVGViewBox, dom::SVGAnimatedRect>
95
  nsSVGViewBox::sSVGAnimatedRectTearoffTable;
96
97
98
/* Implementation of nsSVGViewBox methods */
99
100
void
101
nsSVGViewBox::Init()
102
0
{
103
0
  mHasBaseVal = false;
104
0
  // We shouldn't use mBaseVal for rendering (its usages should be guarded with
105
0
  // "mHasBaseVal" checks), but just in case we do by accident, this will
106
0
  // ensure that we treat it as "none" and ignore its numeric values:
107
0
  mBaseVal.none = true;
108
0
109
0
  mAnimVal = nullptr;
110
0
}
111
112
bool
113
nsSVGViewBox::HasRect() const
114
0
{
115
0
  // Check mAnimVal if we have one; otherwise, check mBaseVal if we have one;
116
0
  // otherwise, just return false (we clearly do not have a rect).
117
0
  const nsSVGViewBoxRect* rect = mAnimVal;
118
0
  if (!rect) {
119
0
    if (!mHasBaseVal) {
120
0
      // no anim val, no base val --> no viewbox rect
121
0
      return false;
122
0
    }
123
0
    rect = &mBaseVal;
124
0
  }
125
0
126
0
  return !rect->none && rect->width >= 0 && rect->height >= 0;
127
0
}
128
129
void
130
nsSVGViewBox::SetAnimValue(const nsSVGViewBoxRect& aRect,
131
                           nsSVGElement *aSVGElement)
132
0
{
133
0
  if (!mAnimVal) {
134
0
    // it's okay if allocation fails - and no point in reporting that
135
0
    mAnimVal = new nsSVGViewBoxRect(aRect);
136
0
  } else {
137
0
    if (aRect == *mAnimVal) {
138
0
      return;
139
0
    }
140
0
    *mAnimVal = aRect;
141
0
  }
142
0
  aSVGElement->DidAnimateViewBox();
143
0
}
144
145
void
146
nsSVGViewBox::SetBaseValue(const nsSVGViewBoxRect& aRect,
147
                           nsSVGElement *aSVGElement)
148
0
{
149
0
  if (!mHasBaseVal || mBaseVal == aRect) {
150
0
    // This method is used to set a single x, y, width
151
0
    // or height value. It can't create a base value
152
0
    // as the other components may be undefined. We record
153
0
    // the new value though, so as not to lose data.
154
0
    mBaseVal = aRect;
155
0
    return;
156
0
  }
157
0
158
0
  nsAttrValue emptyOrOldValue = aSVGElement->WillChangeViewBox();
159
0
160
0
  mBaseVal = aRect;
161
0
  mHasBaseVal = true;
162
0
163
0
  aSVGElement->DidChangeViewBox(emptyOrOldValue);
164
0
  if (mAnimVal) {
165
0
    aSVGElement->AnimationNeedsResample();
166
0
  }
167
0
}
168
169
nsresult
170
nsSVGViewBox::SetBaseValueString(const nsAString& aValue,
171
                                 nsSVGElement *aSVGElement,
172
                                 bool aDoSetAttr)
173
0
{
174
0
  nsSVGViewBoxRect viewBox;
175
0
176
0
  nsresult rv = nsSVGViewBoxRect::FromString(aValue, &viewBox);
177
0
  if (NS_FAILED(rv)) {
178
0
    return rv;
179
0
  }
180
0
  // Comparison against mBaseVal is only valid if we currently have a base val.
181
0
  if (mHasBaseVal && viewBox == mBaseVal) {
182
0
    return NS_OK;
183
0
  }
184
0
185
0
  nsAttrValue emptyOrOldValue;
186
0
  if (aDoSetAttr) {
187
0
    emptyOrOldValue = aSVGElement->WillChangeViewBox();
188
0
  }
189
0
  mHasBaseVal = true;
190
0
  mBaseVal = viewBox;
191
0
192
0
  if (aDoSetAttr) {
193
0
    aSVGElement->DidChangeViewBox(emptyOrOldValue);
194
0
  }
195
0
  if (mAnimVal) {
196
0
    aSVGElement->AnimationNeedsResample();
197
0
  }
198
0
  return NS_OK;
199
0
}
200
201
void
202
nsSVGViewBox::GetBaseValueString(nsAString& aValue) const
203
0
{
204
0
  if (mBaseVal.none) {
205
0
    aValue.AssignLiteral("none");
206
0
    return;
207
0
  }
208
0
  nsTextFormatter::ssprintf(aValue,
209
0
                            u"%g %g %g %g",
210
0
                            (double)mBaseVal.x, (double)mBaseVal.y,
211
0
                            (double)mBaseVal.width, (double)mBaseVal.height);
212
0
}
213
214
215
already_AddRefed<dom::SVGAnimatedRect>
216
nsSVGViewBox::ToSVGAnimatedRect(nsSVGElement* aSVGElement)
217
0
{
218
0
  RefPtr<dom::SVGAnimatedRect> domAnimatedRect =
219
0
    sSVGAnimatedRectTearoffTable.GetTearoff(this);
220
0
  if (!domAnimatedRect) {
221
0
    domAnimatedRect = new dom::SVGAnimatedRect(this, aSVGElement);
222
0
    sSVGAnimatedRectTearoffTable.AddTearoff(this, domAnimatedRect);
223
0
  }
224
0
225
0
  return domAnimatedRect.forget();
226
0
}
227
228
already_AddRefed<dom::SVGIRect>
229
nsSVGViewBox::ToDOMBaseVal(nsSVGElement *aSVGElement)
230
0
{
231
0
  if (!mHasBaseVal || mBaseVal.none) {
232
0
    return nullptr;
233
0
  }
234
0
235
0
  RefPtr<DOMBaseVal> domBaseVal =
236
0
    sBaseSVGViewBoxTearoffTable.GetTearoff(this);
237
0
  if (!domBaseVal) {
238
0
    domBaseVal = new DOMBaseVal(this, aSVGElement);
239
0
    sBaseSVGViewBoxTearoffTable.AddTearoff(this, domBaseVal);
240
0
  }
241
0
242
0
 return domBaseVal.forget();
243
0
}
244
245
nsSVGViewBox::DOMBaseVal::~DOMBaseVal()
246
0
{
247
0
  sBaseSVGViewBoxTearoffTable.RemoveTearoff(mVal);
248
0
}
249
250
already_AddRefed<dom::SVGIRect>
251
nsSVGViewBox::ToDOMAnimVal(nsSVGElement *aSVGElement)
252
0
{
253
0
  if ((mAnimVal && mAnimVal->none) ||
254
0
      (!mAnimVal && (!mHasBaseVal || mBaseVal.none))) {
255
0
    return nullptr;
256
0
  }
257
0
258
0
  RefPtr<DOMAnimVal> domAnimVal =
259
0
    sAnimSVGViewBoxTearoffTable.GetTearoff(this);
260
0
  if (!domAnimVal) {
261
0
    domAnimVal = new DOMAnimVal(this, aSVGElement);
262
0
    sAnimSVGViewBoxTearoffTable.AddTearoff(this, domAnimVal);
263
0
  }
264
0
265
0
  return domAnimVal.forget();
266
0
}
267
268
nsSVGViewBox::DOMAnimVal::~DOMAnimVal()
269
0
{
270
0
  sAnimSVGViewBoxTearoffTable.RemoveTearoff(mVal);
271
0
}
272
273
void
274
nsSVGViewBox::DOMBaseVal::SetX(float aX, ErrorResult& aRv)
275
0
{
276
0
  nsSVGViewBoxRect rect = mVal->GetBaseValue();
277
0
  rect.x = aX;
278
0
  mVal->SetBaseValue(rect, mSVGElement);
279
0
}
280
281
void
282
nsSVGViewBox::DOMBaseVal::SetY(float aY, ErrorResult& aRv)
283
0
{
284
0
  nsSVGViewBoxRect rect = mVal->GetBaseValue();
285
0
  rect.y = aY;
286
0
  mVal->SetBaseValue(rect, mSVGElement);
287
0
}
288
289
void
290
nsSVGViewBox::DOMBaseVal::SetWidth(float aWidth, ErrorResult& aRv)
291
0
{
292
0
  nsSVGViewBoxRect rect = mVal->GetBaseValue();
293
0
  rect.width = aWidth;
294
0
  mVal->SetBaseValue(rect, mSVGElement);
295
0
}
296
297
void
298
nsSVGViewBox::DOMBaseVal::SetHeight(float aHeight, ErrorResult& aRv)
299
0
{
300
0
  nsSVGViewBoxRect rect = mVal->GetBaseValue();
301
0
  rect.height = aHeight;
302
0
  mVal->SetBaseValue(rect, mSVGElement);
303
0
}
304
305
UniquePtr<nsISMILAttr>
306
nsSVGViewBox::ToSMILAttr(nsSVGElement *aSVGElement)
307
0
{
308
0
  return MakeUnique<SMILViewBox>(this, aSVGElement);
309
0
}
310
311
nsresult
312
nsSVGViewBox::SMILViewBox
313
            ::ValueFromString(const nsAString& aStr,
314
                              const dom::SVGAnimationElement* /*aSrcElement*/,
315
                              nsSMILValue& aValue,
316
                              bool& aPreventCachingOfSandwich) const
317
0
{
318
0
  nsSVGViewBoxRect viewBox;
319
0
  nsresult res = nsSVGViewBoxRect::FromString(aStr, &viewBox);
320
0
  if (NS_FAILED(res)) {
321
0
    return res;
322
0
  }
323
0
  nsSMILValue val(&SVGViewBoxSMILType::sSingleton);
324
0
  *static_cast<nsSVGViewBoxRect*>(val.mU.mPtr) = viewBox;
325
0
  aValue = std::move(val);
326
0
  aPreventCachingOfSandwich = false;
327
0
328
0
  return NS_OK;
329
0
}
330
331
nsSMILValue
332
nsSVGViewBox::SMILViewBox::GetBaseValue() const
333
0
{
334
0
  nsSMILValue val(&SVGViewBoxSMILType::sSingleton);
335
0
  *static_cast<nsSVGViewBoxRect*>(val.mU.mPtr) = mVal->mBaseVal;
336
0
  return val;
337
0
}
338
339
void
340
nsSVGViewBox::SMILViewBox::ClearAnimValue()
341
0
{
342
0
  if (mVal->mAnimVal) {
343
0
    mVal->mAnimVal = nullptr;
344
0
    mSVGElement->DidAnimateViewBox();
345
0
  }
346
0
}
347
348
nsresult
349
nsSVGViewBox::SMILViewBox::SetAnimValue(const nsSMILValue& aValue)
350
0
{
351
0
  NS_ASSERTION(aValue.mType == &SVGViewBoxSMILType::sSingleton,
352
0
               "Unexpected type to assign animated value");
353
0
  if (aValue.mType == &SVGViewBoxSMILType::sSingleton) {
354
0
    nsSVGViewBoxRect &vb = *static_cast<nsSVGViewBoxRect*>(aValue.mU.mPtr);
355
0
    mVal->SetAnimValue(vb, mSVGElement);
356
0
  }
357
0
  return NS_OK;
358
0
}