Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/svg/SVGSVGElement.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 "mozilla/ContentEvents.h"
8
#include "mozilla/dom/SVGSVGElement.h"
9
#include "mozilla/dom/SVGSVGElementBinding.h"
10
#include "mozilla/dom/SVGMatrix.h"
11
#include "mozilla/dom/SVGViewElement.h"
12
#include "mozilla/EventDispatcher.h"
13
14
#include "DOMSVGLength.h"
15
#include "DOMSVGNumber.h"
16
#include "DOMSVGPoint.h"
17
#include "nsLayoutStylesheetCache.h"
18
#include "nsSVGAngle.h"
19
#include "nsFrameSelection.h"
20
#include "nsIFrame.h"
21
#include "nsISVGSVGFrame.h"
22
#include "nsSMILAnimationController.h"
23
#include "nsSMILTimeContainer.h"
24
#include "nsSVGDisplayableFrame.h"
25
#include "nsSVGUtils.h"
26
#include "SVGAngle.h"
27
28
NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT_CHECK_PARSER(SVG)
29
30
using namespace mozilla::gfx;
31
32
namespace mozilla {
33
namespace dom {
34
35
using namespace SVGPreserveAspectRatio_Binding;
36
using namespace SVGSVGElement_Binding;
37
38
nsSVGEnumMapping SVGSVGElement::sZoomAndPanMap[] = {
39
  {&nsGkAtoms::disable, SVG_ZOOMANDPAN_DISABLE},
40
  {&nsGkAtoms::magnify, SVG_ZOOMANDPAN_MAGNIFY},
41
  {nullptr, 0}
42
};
43
44
nsSVGElement::EnumInfo SVGSVGElement::sEnumInfo[1] =
45
{
46
  { &nsGkAtoms::zoomAndPan,
47
    sZoomAndPanMap,
48
    SVG_ZOOMANDPAN_MAGNIFY
49
  }
50
};
51
52
NS_IMPL_CYCLE_COLLECTION_INHERITED(DOMSVGTranslatePoint, nsISVGPoint,
53
                                   mElement)
54
55
NS_IMPL_ADDREF_INHERITED(DOMSVGTranslatePoint, nsISVGPoint)
56
NS_IMPL_RELEASE_INHERITED(DOMSVGTranslatePoint, nsISVGPoint)
57
58
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGTranslatePoint)
59
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
60
0
  // We have to qualify nsISVGPoint because NS_GET_IID looks for a class in the
61
0
  // global namespace
62
0
  NS_INTERFACE_MAP_ENTRY(mozilla::nsISVGPoint)
63
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
64
0
NS_INTERFACE_MAP_END
65
66
DOMSVGPoint*
67
DOMSVGTranslatePoint::Copy()
68
0
{
69
0
  return new DOMSVGPoint(mPt.GetX(), mPt.GetY());
70
0
}
71
72
nsISupports*
73
DOMSVGTranslatePoint::GetParentObject()
74
0
{
75
0
  return ToSupports(mElement);
76
0
}
77
78
void
79
DOMSVGTranslatePoint::SetX(float aValue, ErrorResult& rv)
80
0
{
81
0
  mElement->SetCurrentTranslate(aValue, mPt.GetY());
82
0
}
83
84
void
85
DOMSVGTranslatePoint::SetY(float aValue, ErrorResult& rv)
86
0
{
87
0
  mElement->SetCurrentTranslate(mPt.GetX(), aValue);
88
0
}
89
90
already_AddRefed<nsISVGPoint>
91
DOMSVGTranslatePoint::MatrixTransform(SVGMatrix& matrix)
92
0
{
93
0
  float a = matrix.A(), b = matrix.B(), c = matrix.C();
94
0
  float d = matrix.D(), e = matrix.E(), f = matrix.F();
95
0
  float x = mPt.GetX();
96
0
  float y = mPt.GetY();
97
0
98
0
  nsCOMPtr<nsISVGPoint> point = new DOMSVGPoint(a*x + c*y + e, b*x + d*y + f);
99
0
  return point.forget();
100
0
}
101
102
JSObject*
103
SVGSVGElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
104
0
{
105
0
  return SVGSVGElement_Binding::Wrap(aCx, this, aGivenProto);
106
0
}
107
108
//----------------------------------------------------------------------
109
// nsISupports methods
110
111
NS_IMPL_CYCLE_COLLECTION_CLASS(SVGSVGElement)
112
113
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGSVGElement,
114
0
                                                SVGSVGElementBase)
115
0
  if (tmp->mTimedDocumentRoot) {
116
0
    tmp->mTimedDocumentRoot->Unlink();
117
0
  }
118
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
119
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGSVGElement,
120
0
                                                  SVGSVGElementBase)
121
0
  if (tmp->mTimedDocumentRoot) {
122
0
    tmp->mTimedDocumentRoot->Traverse(&cb);
123
0
  }
124
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
125
126
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(SVGSVGElement,
127
                                               SVGSVGElementBase)
128
129
SVGView::SVGView()
130
0
{
131
0
  mZoomAndPan.Init(SVGSVGElement::ZOOMANDPAN,
132
0
                   SVG_ZOOMANDPAN_MAGNIFY);
133
0
  mViewBox.Init();
134
0
  mPreserveAspectRatio.Init();
135
0
}
136
137
//----------------------------------------------------------------------
138
// Implementation
139
140
SVGSVGElement::SVGSVGElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
141
                             FromParser aFromParser)
142
  : SVGSVGElementBase(std::move(aNodeInfo)),
143
    mCurrentTranslate(0.0f, 0.0f),
144
    mCurrentScale(1.0f),
145
    mPreviousTranslate(0.0f, 0.0f),
146
    mPreviousScale(1.0f),
147
    mStartAnimationOnBindToTree(aFromParser == NOT_FROM_PARSER ||
148
                                aFromParser == FROM_PARSER_FRAGMENT ||
149
                                aFromParser == FROM_PARSER_XSLT),
150
    mImageNeedsTransformInvalidation(false)
151
0
{
152
0
}
153
154
SVGSVGElement::~SVGSVGElement()
155
0
{
156
0
}
157
158
//----------------------------------------------------------------------
159
// nsINode methods
160
161
NS_IMPL_ELEMENT_CLONE_WITH_INIT_AND_PARSER(SVGSVGElement)
162
163
//----------------------------------------------------------------------
164
// nsIDOMSVGSVGElement methods:
165
166
already_AddRefed<SVGAnimatedLength>
167
SVGSVGElement::X()
168
0
{
169
0
  return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this);
170
0
}
171
172
already_AddRefed<SVGAnimatedLength>
173
SVGSVGElement::Y()
174
0
{
175
0
  return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this);
176
0
}
177
178
already_AddRefed<SVGAnimatedLength>
179
SVGSVGElement::Width()
180
0
{
181
0
  return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);
182
0
}
183
184
already_AddRefed<SVGAnimatedLength>
185
SVGSVGElement::Height()
186
0
{
187
0
  return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
188
0
}
189
190
bool
191
SVGSVGElement::UseCurrentView()
192
0
{
193
0
  return mSVGView || mCurrentViewID;
194
0
}
195
196
float
197
SVGSVGElement::CurrentScale()
198
0
{
199
0
  return mCurrentScale;
200
0
}
201
202
0
#define CURRENT_SCALE_MAX 16.0f
203
0
#define CURRENT_SCALE_MIN 0.0625f
204
205
void
206
SVGSVGElement::SetCurrentScale(float aCurrentScale)
207
0
{
208
0
  SetCurrentScaleTranslate(aCurrentScale,
209
0
    mCurrentTranslate.GetX(), mCurrentTranslate.GetY());
210
0
}
211
212
already_AddRefed<nsISVGPoint>
213
SVGSVGElement::CurrentTranslate()
214
0
{
215
0
  nsCOMPtr<nsISVGPoint> point = new DOMSVGTranslatePoint(&mCurrentTranslate, this);
216
0
  return point.forget();
217
0
}
218
219
uint32_t
220
SVGSVGElement::SuspendRedraw(uint32_t max_wait_milliseconds)
221
0
{
222
0
  // suspendRedraw is a no-op in Mozilla, so it doesn't matter what
223
0
  // we return
224
0
  return 1;
225
0
}
226
227
void
228
SVGSVGElement::UnsuspendRedraw(uint32_t suspend_handle_id)
229
0
{
230
0
  // no-op
231
0
}
232
233
void
234
SVGSVGElement::UnsuspendRedrawAll()
235
0
{
236
0
  // no-op
237
0
}
238
239
void
240
SVGSVGElement::ForceRedraw()
241
0
{
242
0
  // no-op
243
0
}
244
245
void
246
SVGSVGElement::PauseAnimations()
247
0
{
248
0
  if (mTimedDocumentRoot) {
249
0
    mTimedDocumentRoot->Pause(nsSMILTimeContainer::PAUSE_SCRIPT);
250
0
  }
251
0
  // else we're not the outermost <svg> or not bound to a tree, so silently fail
252
0
}
253
254
void
255
SVGSVGElement::UnpauseAnimations()
256
0
{
257
0
  if (mTimedDocumentRoot) {
258
0
    mTimedDocumentRoot->Resume(nsSMILTimeContainer::PAUSE_SCRIPT);
259
0
  }
260
0
  // else we're not the outermost <svg> or not bound to a tree, so silently fail
261
0
}
262
263
bool
264
SVGSVGElement::AnimationsPaused()
265
0
{
266
0
  nsSMILTimeContainer* root = GetTimedDocumentRoot();
267
0
  return root && root->IsPausedByType(nsSMILTimeContainer::PAUSE_SCRIPT);
268
0
}
269
270
float
271
SVGSVGElement::GetCurrentTime()
272
0
{
273
0
  nsSMILTimeContainer* root = GetTimedDocumentRoot();
274
0
  if (root) {
275
0
    double fCurrentTimeMs = double(root->GetCurrentTime());
276
0
    return (float)(fCurrentTimeMs / PR_MSEC_PER_SEC);
277
0
  } else {
278
0
    return 0.f;
279
0
  }
280
0
}
281
282
void
283
SVGSVGElement::SetCurrentTime(float seconds)
284
0
{
285
0
  if (mTimedDocumentRoot) {
286
0
    // Make sure the timegraph is up-to-date
287
0
    FlushAnimations();
288
0
    double fMilliseconds = double(seconds) * PR_MSEC_PER_SEC;
289
0
    // Round to nearest whole number before converting, to avoid precision
290
0
    // errors
291
0
    nsSMILTime lMilliseconds = int64_t(NS_round(fMilliseconds));
292
0
    mTimedDocumentRoot->SetCurrentTime(lMilliseconds);
293
0
    AnimationNeedsResample();
294
0
    // Trigger synchronous sample now, to:
295
0
    //  - Make sure we get an up-to-date paint after this method
296
0
    //  - re-enable event firing (it got disabled during seeking, and it
297
0
    //  doesn't get re-enabled until the first sample after the seek -- so
298
0
    //  let's make that happen now.)
299
0
    FlushAnimations();
300
0
  }
301
0
  // else we're not the outermost <svg> or not bound to a tree, so silently fail
302
0
}
303
304
void
305
SVGSVGElement::DeselectAll()
306
0
{
307
0
  nsIFrame* frame = GetPrimaryFrame();
308
0
  if (frame) {
309
0
    RefPtr<nsFrameSelection> frameSelection = frame->GetFrameSelection();
310
0
    frameSelection->ClearNormalSelection();
311
0
  }
312
0
}
313
314
already_AddRefed<DOMSVGNumber>
315
SVGSVGElement::CreateSVGNumber()
316
0
{
317
0
  RefPtr<DOMSVGNumber> number = new DOMSVGNumber(ToSupports(this));
318
0
  return number.forget();
319
0
}
320
321
already_AddRefed<DOMSVGLength>
322
SVGSVGElement::CreateSVGLength()
323
0
{
324
0
  nsCOMPtr<DOMSVGLength> length = new DOMSVGLength();
325
0
  return length.forget();
326
0
}
327
328
already_AddRefed<SVGAngle>
329
SVGSVGElement::CreateSVGAngle()
330
0
{
331
0
  nsSVGAngle* angle = new nsSVGAngle();
332
0
  angle->Init();
333
0
  RefPtr<SVGAngle> svgangle = new SVGAngle(angle, this, SVGAngle::CreatedValue);
334
0
  return svgangle.forget();
335
0
}
336
337
already_AddRefed<nsISVGPoint>
338
SVGSVGElement::CreateSVGPoint()
339
0
{
340
0
  nsCOMPtr<nsISVGPoint> point = new DOMSVGPoint(0, 0);
341
0
  return point.forget();
342
0
}
343
344
already_AddRefed<SVGMatrix>
345
SVGSVGElement::CreateSVGMatrix()
346
0
{
347
0
  RefPtr<SVGMatrix> matrix = new SVGMatrix();
348
0
  return matrix.forget();
349
0
}
350
351
already_AddRefed<SVGIRect>
352
SVGSVGElement::CreateSVGRect()
353
0
{
354
0
  return NS_NewSVGRect(this);
355
0
}
356
357
already_AddRefed<SVGTransform>
358
SVGSVGElement::CreateSVGTransform()
359
0
{
360
0
  RefPtr<SVGTransform> transform = new SVGTransform();
361
0
  return transform.forget();
362
0
}
363
364
already_AddRefed<SVGTransform>
365
SVGSVGElement::CreateSVGTransformFromMatrix(SVGMatrix& matrix)
366
0
{
367
0
  RefPtr<SVGTransform> transform = new SVGTransform(matrix.GetMatrix());
368
0
  return transform.forget();
369
0
}
370
371
//----------------------------------------------------------------------
372
// helper method for implementing SetCurrentScale/Translate
373
374
void
375
SVGSVGElement::SetCurrentScaleTranslate(float s, float x, float y)
376
0
{
377
0
  if (s == mCurrentScale &&
378
0
      x == mCurrentTranslate.GetX() && y == mCurrentTranslate.GetY()) {
379
0
    return;
380
0
  }
381
0
382
0
  // Prevent bizarre behaviour and maxing out of CPU and memory by clamping
383
0
  if (s < CURRENT_SCALE_MIN)
384
0
    s = CURRENT_SCALE_MIN;
385
0
  else if (s > CURRENT_SCALE_MAX)
386
0
    s = CURRENT_SCALE_MAX;
387
0
388
0
  // IMPORTANT: If either mCurrentTranslate *or* mCurrentScale is changed then
389
0
  // mPreviousTranslate_x, mPreviousTranslate_y *and* mPreviousScale must all
390
0
  // be updated otherwise SVGZoomEvents will end up with invalid data. I.e. an
391
0
  // SVGZoomEvent's properties previousScale and previousTranslate must contain
392
0
  // the state of currentScale and currentTranslate immediately before the
393
0
  // change that caused the event's dispatch, which is *not* necessarily the
394
0
  // same thing as the values of currentScale and currentTranslate prior to
395
0
  // their own last change.
396
0
  //
397
0
  // XXX This comment is out-of-date due to removal of SVGZoomEvent.  Can we
398
0
  // remove some of this code?
399
0
  mPreviousScale = mCurrentScale;
400
0
  mPreviousTranslate = mCurrentTranslate;
401
0
402
0
  mCurrentScale = s;
403
0
  mCurrentTranslate = SVGPoint(x, y);
404
0
405
0
  // now dispatch the appropriate event if we are the root element
406
0
  nsIDocument* doc = GetUncomposedDoc();
407
0
  if (doc) {
408
0
    nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
409
0
    if (presShell && IsRoot()) {
410
0
      nsEventStatus status = nsEventStatus_eIgnore;
411
0
      if (mPreviousScale == mCurrentScale) {
412
0
        WidgetEvent svgScrollEvent(true, eSVGScroll);
413
0
        presShell->HandleDOMEventWithTarget(this, &svgScrollEvent, &status);
414
0
      }
415
0
      InvalidateTransformNotifyFrame();
416
0
    }
417
0
  }
418
0
}
419
420
void
421
SVGSVGElement::SetCurrentTranslate(float x, float y)
422
0
{
423
0
  SetCurrentScaleTranslate(mCurrentScale, x, y);
424
0
}
425
426
//----------------------------------------------------------------------
427
// SVGZoomAndPanValues
428
uint16_t
429
SVGSVGElement::ZoomAndPan()
430
0
{
431
0
  return mEnumAttributes[ZOOMANDPAN].GetAnimValue();
432
0
}
433
434
void
435
SVGSVGElement::SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv)
436
0
{
437
0
  if (aZoomAndPan == SVG_ZOOMANDPAN_DISABLE ||
438
0
      aZoomAndPan == SVG_ZOOMANDPAN_MAGNIFY) {
439
0
    mEnumAttributes[ZOOMANDPAN].SetBaseValue(aZoomAndPan, this);
440
0
    return;
441
0
  }
442
0
443
0
  rv.ThrowRangeError<MSG_INVALID_ZOOMANDPAN_VALUE_ERROR>();
444
0
}
445
446
//----------------------------------------------------------------------
447
nsSMILTimeContainer*
448
SVGSVGElement::GetTimedDocumentRoot()
449
0
{
450
0
  if (mTimedDocumentRoot) {
451
0
    return mTimedDocumentRoot;
452
0
  }
453
0
454
0
  // We must not be the outermost <svg> element, try to find it
455
0
  SVGSVGElement *outerSVGElement =
456
0
    SVGContentUtils::GetOuterSVGElement(this);
457
0
458
0
  if (outerSVGElement) {
459
0
    return outerSVGElement->GetTimedDocumentRoot();
460
0
  }
461
0
  // invalid structure
462
0
  return nullptr;
463
0
}
464
//----------------------------------------------------------------------
465
// nsSVGElement
466
nsresult
467
SVGSVGElement::BindToTree(nsIDocument* aDocument,
468
                          nsIContent* aParent,
469
                          nsIContent* aBindingParent)
470
0
{
471
0
  nsSMILAnimationController* smilController = nullptr;
472
0
473
0
  if (aDocument) {
474
0
    smilController = aDocument->GetAnimationController();
475
0
    if (smilController) {
476
0
      // SMIL is enabled in this document
477
0
      if (WillBeOutermostSVG(aParent, aBindingParent)) {
478
0
        // We'll be the outermost <svg> element.  We'll need a time container.
479
0
        if (!mTimedDocumentRoot) {
480
0
          mTimedDocumentRoot = new nsSMILTimeContainer();
481
0
        }
482
0
      } else {
483
0
        // We're a child of some other <svg> element, so we don't need our own
484
0
        // time container. However, we need to make sure that we'll get a
485
0
        // kick-start if we get promoted to be outermost later on.
486
0
        mTimedDocumentRoot = nullptr;
487
0
        mStartAnimationOnBindToTree = true;
488
0
      }
489
0
    }
490
0
  }
491
0
492
0
  nsresult rv = SVGGraphicsElement::BindToTree(aDocument, aParent,
493
0
                                              aBindingParent);
494
0
  NS_ENSURE_SUCCESS(rv,rv);
495
0
496
0
  if (mTimedDocumentRoot && smilController) {
497
0
    rv = mTimedDocumentRoot->SetParent(smilController);
498
0
    if (mStartAnimationOnBindToTree) {
499
0
      mTimedDocumentRoot->Begin();
500
0
      mStartAnimationOnBindToTree = false;
501
0
    }
502
0
  }
503
0
504
0
  return rv;
505
0
}
506
507
void
508
SVGSVGElement::UnbindFromTree(bool aDeep, bool aNullParent)
509
0
{
510
0
  if (mTimedDocumentRoot) {
511
0
    mTimedDocumentRoot->SetParent(nullptr);
512
0
  }
513
0
514
0
  SVGGraphicsElement::UnbindFromTree(aDeep, aNullParent);
515
0
}
516
517
nsSVGAnimatedTransformList*
518
SVGSVGElement::GetAnimatedTransformList(uint32_t aFlags)
519
0
{
520
0
  if (!(aFlags & DO_ALLOCATE) && mSVGView && mSVGView->mTransforms) {
521
0
    return mSVGView->mTransforms;
522
0
  }
523
0
  return SVGGraphicsElement::GetAnimatedTransformList(aFlags);
524
0
}
525
526
void
527
SVGSVGElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
528
0
{
529
0
  if (aVisitor.mEvent->mMessage == eSVGLoad) {
530
0
    if (mTimedDocumentRoot) {
531
0
      mTimedDocumentRoot->Begin();
532
0
      // Set 'resample needed' flag, so that if any script calls a DOM method
533
0
      // that requires up-to-date animations before our first sample callback,
534
0
      // we'll force a synchronous sample.
535
0
      AnimationNeedsResample();
536
0
    }
537
0
  }
538
0
  SVGSVGElementBase::GetEventTargetParent(aVisitor);
539
0
}
540
541
bool
542
SVGSVGElement::IsEventAttributeNameInternal(nsAtom* aName)
543
0
{
544
0
  /* The events in EventNameType_SVGSVG are for events that are only
545
0
     applicable to outermost 'svg' elements. We don't check if we're an outer
546
0
     'svg' element in case we're not inserted into the document yet, but since
547
0
     the target of the events in question will always be the outermost 'svg'
548
0
     element, this shouldn't cause any real problems.
549
0
  */
550
0
  return nsContentUtils::IsEventAttributeName(aName,
551
0
         (EventNameType_SVGGraphic | EventNameType_SVGSVG));
552
0
}
553
554
//----------------------------------------------------------------------
555
// public helpers:
556
557
int32_t
558
SVGSVGElement::GetIntrinsicWidth()
559
0
{
560
0
  if (mLengthAttributes[ATTR_WIDTH].IsPercentage()) {
561
0
    return -1;
562
0
  }
563
0
  // Passing |this| as a SVGViewportElement* invokes the variant of GetAnimValue
564
0
  // that uses the passed argument as the context, but that's fine since we
565
0
  // know the length isn't a percentage so the context won't be used (and we
566
0
  // need to pass the element to be able to resolve em/ex units).
567
0
  float width = mLengthAttributes[ATTR_WIDTH].GetAnimValue(this);
568
0
  return nsSVGUtils::ClampToInt(width);
569
0
}
570
571
int32_t
572
SVGSVGElement::GetIntrinsicHeight()
573
0
{
574
0
  if (mLengthAttributes[ATTR_HEIGHT].IsPercentage()) {
575
0
    return -1;
576
0
  }
577
0
  // Passing |this| as a SVGViewportElement* invokes the variant of GetAnimValue
578
0
  // that uses the passed argument as the context, but that's fine since we
579
0
  // know the length isn't a percentage so the context won't be used (and we
580
0
  // need to pass the element to be able to resolve em/ex units).
581
0
  float height = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(this);
582
0
  return nsSVGUtils::ClampToInt(height);
583
0
}
584
585
void
586
SVGSVGElement::FlushImageTransformInvalidation()
587
0
{
588
0
  MOZ_ASSERT(!GetParent(), "Should only be called on root node");
589
0
  MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(),
590
0
             "Should only be called on image documents");
591
0
592
0
  if (mImageNeedsTransformInvalidation) {
593
0
    InvalidateTransformNotifyFrame();
594
0
    mImageNeedsTransformInvalidation = false;
595
0
  }
596
0
}
597
598
//----------------------------------------------------------------------
599
// implementation helpers
600
601
bool
602
SVGSVGElement::WillBeOutermostSVG(nsIContent* aParent,
603
                                  nsIContent* aBindingParent) const
604
0
{
605
0
  nsIContent* parent = aBindingParent ? aBindingParent : aParent;
606
0
607
0
  while (parent && parent->IsSVGElement()) {
608
0
    if (parent->IsSVGElement(nsGkAtoms::foreignObject)) {
609
0
      // SVG in a foreignObject must have its own <svg> (nsSVGOuterSVGFrame).
610
0
      return false;
611
0
    }
612
0
    if (parent->IsSVGElement(nsGkAtoms::svg)) {
613
0
      return false;
614
0
    }
615
0
    parent = parent->GetParent();
616
0
  }
617
0
618
0
  return true;
619
0
}
620
621
void
622
SVGSVGElement::InvalidateTransformNotifyFrame()
623
0
{
624
0
  nsISVGSVGFrame* svgframe = do_QueryFrame(GetPrimaryFrame());
625
0
  // might fail this check if we've failed conditional processing
626
0
  if (svgframe) {
627
0
    svgframe->NotifyViewportOrTransformChanged(
628
0
                nsSVGDisplayableFrame::TRANSFORM_CHANGED);
629
0
  }
630
0
}
631
632
nsSVGElement::EnumAttributesInfo
633
SVGSVGElement::GetEnumInfo()
634
0
{
635
0
  return EnumAttributesInfo(mEnumAttributes, sEnumInfo,
636
0
                            ArrayLength(sEnumInfo));
637
0
}
638
639
void
640
SVGSVGElement::
641
  SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR)
642
0
{
643
#ifdef DEBUG
644
  MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(),
645
             "should only override preserveAspectRatio in images");
646
#endif
647
648
0
  bool hasViewBoxRect = HasViewBoxRect();
649
0
  if (!hasViewBoxRect && ShouldSynthesizeViewBox()) {
650
0
    // My non-<svg:image> clients will have been painting me with a synthesized
651
0
    // viewBox, but my <svg:image> client that's about to paint me now does NOT
652
0
    // want that.  Need to tell ourselves to flush our transform.
653
0
    mImageNeedsTransformInvalidation = true;
654
0
  }
655
0
656
0
  if (!hasViewBoxRect) {
657
0
    return; // preserveAspectRatio irrelevant (only matters if we have viewBox)
658
0
  }
659
0
660
0
  if (SetPreserveAspectRatioProperty(aPAR)) {
661
0
    mImageNeedsTransformInvalidation = true;
662
0
  }
663
0
}
664
665
void
666
SVGSVGElement::ClearImageOverridePreserveAspectRatio()
667
0
{
668
#ifdef DEBUG
669
  MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(),
670
             "should only override image preserveAspectRatio in images");
671
#endif
672
673
0
  if (!HasViewBoxRect() && ShouldSynthesizeViewBox()) {
674
0
    // My non-<svg:image> clients will want to paint me with a synthesized
675
0
    // viewBox, but my <svg:image> client that just painted me did NOT
676
0
    // use that.  Need to tell ourselves to flush our transform.
677
0
    mImageNeedsTransformInvalidation = true;
678
0
  }
679
0
680
0
  if (ClearPreserveAspectRatioProperty()) {
681
0
    mImageNeedsTransformInvalidation = true;
682
0
  }
683
0
}
684
685
bool
686
SVGSVGElement::SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR)
687
0
{
688
0
  SVGPreserveAspectRatio* pAROverridePtr = new SVGPreserveAspectRatio(aPAR);
689
0
  nsresult rv = SetProperty(nsGkAtoms::overridePreserveAspectRatio,
690
0
                            pAROverridePtr,
691
0
                            nsINode::DeleteProperty<SVGPreserveAspectRatio>,
692
0
                            true);
693
0
  MOZ_ASSERT(rv != NS_PROPTABLE_PROP_OVERWRITTEN,
694
0
             "Setting override value when it's already set...?");
695
0
696
0
  if (MOZ_UNLIKELY(NS_FAILED(rv))) {
697
0
    // property-insertion failed (e.g. OOM in property-table code)
698
0
    delete pAROverridePtr;
699
0
    return false;
700
0
  }
701
0
  return true;
702
0
}
703
704
const SVGPreserveAspectRatio*
705
SVGSVGElement::GetPreserveAspectRatioProperty() const
706
0
{
707
0
  void* valPtr = GetProperty(nsGkAtoms::overridePreserveAspectRatio);
708
0
  if (valPtr) {
709
0
    return static_cast<SVGPreserveAspectRatio*>(valPtr);
710
0
  }
711
0
  return nullptr;
712
0
}
713
714
bool
715
SVGSVGElement::ClearPreserveAspectRatioProperty()
716
0
{
717
0
  void* valPtr = UnsetProperty(nsGkAtoms::overridePreserveAspectRatio);
718
0
  bool didHaveProperty = !!valPtr;
719
0
  delete static_cast<SVGPreserveAspectRatio*>(valPtr);
720
0
  return didHaveProperty;
721
0
}
722
723
724
SVGPreserveAspectRatio
725
SVGSVGElement::GetPreserveAspectRatioWithOverride() const
726
0
{
727
0
  nsIDocument* doc = GetUncomposedDoc();
728
0
  if (doc && doc->IsBeingUsedAsImage()) {
729
0
    const SVGPreserveAspectRatio *pAROverridePtr = GetPreserveAspectRatioProperty();
730
0
    if (pAROverridePtr) {
731
0
      return *pAROverridePtr;
732
0
    }
733
0
  }
734
0
735
0
  SVGViewElement* viewElement = GetCurrentViewElement();
736
0
737
0
  // This check is equivalent to "!HasViewBoxRect() && ShouldSynthesizeViewBox()".
738
0
  // We're just holding onto the viewElement that HasViewBoxRect() would look up,
739
0
  // so that we don't have to look it up again later.
740
0
  if (!((viewElement && viewElement->mViewBox.HasRect()) ||
741
0
        (mSVGView && mSVGView->mViewBox.HasRect()) ||
742
0
        mViewBox.HasRect()) &&
743
0
      ShouldSynthesizeViewBox()) {
744
0
    // If we're synthesizing a viewBox, use preserveAspectRatio="none";
745
0
    return SVGPreserveAspectRatio(SVG_PRESERVEASPECTRATIO_NONE, SVG_MEETORSLICE_SLICE);
746
0
  }
747
0
748
0
  if (viewElement && viewElement->mPreserveAspectRatio.IsExplicitlySet()) {
749
0
    return viewElement->mPreserveAspectRatio.GetAnimValue();
750
0
  }
751
0
  if (mSVGView && mSVGView->mPreserveAspectRatio.IsExplicitlySet()) {
752
0
    return mSVGView->mPreserveAspectRatio.GetAnimValue();
753
0
  }
754
0
  return mPreserveAspectRatio.GetAnimValue();
755
0
}
756
757
SVGViewElement*
758
SVGSVGElement::GetCurrentViewElement() const
759
0
{
760
0
  if (mCurrentViewID) {
761
0
    //XXXsmaug It is unclear how this should work in case we're in Shadow DOM.
762
0
    nsIDocument* doc = GetUncomposedDoc();
763
0
    if (doc) {
764
0
      Element *element = doc->GetElementById(*mCurrentViewID);
765
0
      if (element && element->IsSVGElement(nsGkAtoms::view)) {
766
0
        return static_cast<SVGViewElement*>(element);
767
0
      }
768
0
    }
769
0
  }
770
0
  return nullptr;
771
0
}
772
773
const nsSVGViewBox&
774
SVGSVGElement::GetViewBoxInternal() const
775
0
{
776
0
  SVGViewElement* viewElement = GetCurrentViewElement();
777
0
778
0
  if (viewElement && viewElement->mViewBox.HasRect()) {
779
0
    return viewElement->mViewBox;
780
0
  } else if (mSVGView && mSVGView->mViewBox.HasRect()) {
781
0
    return mSVGView->mViewBox;
782
0
  }
783
0
784
0
  return mViewBox;
785
0
}
786
787
nsSVGAnimatedTransformList*
788
SVGSVGElement::GetTransformInternal() const
789
0
{
790
0
  return (mSVGView && mSVGView->mTransforms)
791
0
         ? mSVGView->mTransforms: mTransforms;
792
0
}
793
794
} // namespace dom
795
} // namespace mozilla