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