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