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 #ifndef mozilla_dom_SVGSVGElement_h
8 #define mozilla_dom_SVGSVGElement_h
9 
10 #include "mozilla/dom/FromParser.h"
11 #include "nsAutoPtr.h"
12 #include "nsIContentInlines.h"
13 #include "nsISVGPoint.h"
14 #include "nsSVGEnum.h"
15 #include "nsSVGLength2.h"
16 #include "SVGGraphicsElement.h"
17 #include "SVGImageContext.h"
18 #include "nsSVGViewBox.h"
19 #include "SVGPreserveAspectRatio.h"
20 #include "SVGAnimatedPreserveAspectRatio.h"
21 #include "mozilla/Attributes.h"
22 
23 nsresult NS_NewSVGSVGElement(nsIContent **aResult,
24                              already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
25                              mozilla::dom::FromParser aFromParser);
26 
27 class nsSMILTimeContainer;
28 class nsSVGOuterSVGFrame;
29 class nsSVGInnerSVGFrame;
30 
31 namespace mozilla {
32 class AutoSVGRenderingState;
33 class DOMSVGAnimatedPreserveAspectRatio;
34 class DOMSVGLength;
35 class DOMSVGNumber;
36 class EventChainPreVisitor;
37 class SVGFragmentIdentifier;
38 class AutoSVGViewHandler;
39 
40 namespace dom {
41 class SVGAngle;
42 class SVGAnimatedRect;
43 class SVGMatrix;
44 class SVGTransform;
45 class SVGViewElement;
46 class SVGIRect;
47 
48 class SVGSVGElement;
49 
50 class DOMSVGTranslatePoint final : public nsISVGPoint {
51 public:
DOMSVGTranslatePoint(SVGPoint * aPt,SVGSVGElement * aElement)52   DOMSVGTranslatePoint(SVGPoint* aPt, SVGSVGElement *aElement)
53     : nsISVGPoint(aPt, true), mElement(aElement) {}
54 
DOMSVGTranslatePoint(DOMSVGTranslatePoint * aPt)55   explicit DOMSVGTranslatePoint(DOMSVGTranslatePoint* aPt)
56     : nsISVGPoint(&aPt->mPt, true), mElement(aPt->mElement) {}
57 
58   NS_DECL_ISUPPORTS_INHERITED
59   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DOMSVGTranslatePoint, nsISVGPoint)
60 
61   virtual DOMSVGPoint* Copy() override;
62 
63   // WebIDL
X()64   virtual float X() override { return mPt.GetX(); }
Y()65   virtual float Y() override { return mPt.GetY(); }
66   virtual void SetX(float aValue, ErrorResult& rv) override;
67   virtual void SetY(float aValue, ErrorResult& rv) override;
68   virtual already_AddRefed<nsISVGPoint> MatrixTransform(SVGMatrix& matrix) override;
69 
70   virtual nsISupports* GetParentObject() override;
71 
72   RefPtr<SVGSVGElement> mElement;
73 
74 private:
~DOMSVGTranslatePoint()75   ~DOMSVGTranslatePoint() {}
76 };
77 
78 class svgFloatSize {
79 public:
svgFloatSize(float aWidth,float aHeight)80   svgFloatSize(float aWidth, float aHeight)
81     : width(aWidth)
82     , height(aHeight)
83   {}
84   bool operator!=(const svgFloatSize& rhs) {
85     return width != rhs.width || height != rhs.height;
86   }
87   float width;
88   float height;
89 };
90 
91 // Stores svgView arguments of SVG fragment identifiers.
92 class SVGView {
93   friend class mozilla::AutoSVGViewHandler;
94   friend class mozilla::dom::SVGSVGElement;
95 public:
96   SVGView();
97 
98 private:
99   nsSVGEnum                             mZoomAndPan;
100   nsSVGViewBox                          mViewBox;
101   SVGAnimatedPreserveAspectRatio        mPreserveAspectRatio;
102   nsAutoPtr<nsSVGAnimatedTransformList> mTransforms;
103 };
104 
105 typedef SVGGraphicsElement SVGSVGElementBase;
106 
107 class SVGSVGElement final : public SVGSVGElementBase
108 {
109   friend class ::nsSVGOuterSVGFrame;
110   friend class ::nsSVGInnerSVGFrame;
111   friend class mozilla::dom::SVGView;
112   friend class mozilla::SVGFragmentIdentifier;
113   friend class mozilla::AutoSVGViewHandler;
114   friend class mozilla::AutoSVGRenderingState;
115 
116   SVGSVGElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
117                 FromParser aFromParser);
118   virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
119 
120   friend nsresult (::NS_NewSVGSVGElement(nsIContent **aResult,
121                                          already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
122                                          mozilla::dom::FromParser aFromParser));
123 
124   ~SVGSVGElement();
125 
126 public:
127   // interfaces:
128   NS_DECL_ISUPPORTS_INHERITED
129   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGSVGElement, SVGSVGElementBase)
130 
131   /**
132    * For use by zoom controls to allow currentScale, currentTranslate.x and
133    * currentTranslate.y to be set by a single operation that dispatches a
134    * single SVGZoom event (instead of one SVGZoom and two SVGScroll events).
135    */
136   void SetCurrentScaleTranslate(float s, float x, float y);
137 
138   /**
139    * Retrieve the value of currentScale and currentTranslate.
140    */
GetCurrentTranslate()141   const SVGPoint& GetCurrentTranslate() { return mCurrentTranslate; }
GetCurrentScale()142   float GetCurrentScale() { return mCurrentScale; }
143 
144   /**
145    * Retrieve the value of currentScale, currentTranslate.x or
146    * currentTranslate.y prior to the last change made to any one of them.
147    */
GetPreviousTranslate()148   const SVGPoint& GetPreviousTranslate() { return mPreviousTranslate; }
GetPreviousScale()149   float GetPreviousScale() { return mPreviousScale; }
150 
151   nsSMILTimeContainer* GetTimedDocumentRoot();
152 
153   // nsIContent interface
154   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override;
155   virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override;
156 
157   virtual bool IsEventAttributeName(nsIAtom* aName) override;
158 
159   // nsSVGElement specializations:
160   virtual gfxMatrix PrependLocalTransformsTo(
161     const gfxMatrix &aMatrix,
162     SVGTransformTypes aWhich = eAllTransforms) const override;
163   virtual nsSVGAnimatedTransformList*
164 	  GetAnimatedTransformList(uint32_t aFlags = 0) override;
165   virtual bool HasValidDimensions() const override;
166 
167   // SVGSVGElement methods:
168   float GetLength(uint8_t mCtxType);
169 
170   // public helpers:
171 
172   /**
173    * Returns -1 if the width/height is a percentage, else returns the user unit
174    * length clamped to fit in a int32_t.
175    * XXX see bug 1112533 comment 3 - we should fix drawImage so that we can
176    * change these methods to make zero the error flag for percentages.
177    */
178   int32_t GetIntrinsicWidth();
179   int32_t GetIntrinsicHeight();
180 
181   /**
182    * Returns true if this element has a base/anim value for its "viewBox"
183    * attribute that defines a viewBox rectangle with finite values, or
184    * if there is a view element overriding this element's viewBox and it
185    * has a valid viewBox.
186    *
187    * Note that this does not check whether we need to synthesize a viewBox,
188    * so you must call ShouldSynthesizeViewBox() if you need to check that too.
189    *
190    * Note also that this method does not pay attention to whether the width or
191    * height values of the viewBox rect are positive!
192    */
193   bool HasViewBoxRect() const;
194 
195   /**
196    * Returns true if we should synthesize a viewBox for ourselves (that is, if
197    * we're the root element in an image document, and we're not currently being
198    * painted for an <svg:image> element).
199    *
200    * Only call this method if HasViewBoxRect() returns false.
201    */
202   bool ShouldSynthesizeViewBox() const;
203 
HasViewBoxOrSyntheticViewBox()204   bool HasViewBoxOrSyntheticViewBox() const {
205     return HasViewBoxRect() || ShouldSynthesizeViewBox();
206   }
207 
208   gfx::Matrix GetViewBoxTransform() const;
209 
HasChildrenOnlyTransform()210   bool HasChildrenOnlyTransform() const {
211     return mHasChildrenOnlyTransform;
212   }
213 
214   void UpdateHasChildrenOnlyTransform();
215 
216   enum ChildrenOnlyTransformChangedFlags {
217     eDuringReflow = 1
218   };
219 
220   /**
221    * This method notifies the style system that the overflow rects of our
222    * immediate childrens' frames need to be updated. It is called by our own
223    * frame when changes (e.g. to currentScale) cause our children-only
224    * transform to change.
225    *
226    * The reason we have this method instead of overriding
227    * GetAttributeChangeHint is because we need to act on non-attribute (e.g.
228    * currentScale) changes in addition to attribute (e.g. viewBox) changes.
229    */
230   void ChildrenOnlyTransformChanged(uint32_t aFlags = 0);
231 
232   // This services any pending notifications for the transform on on this root
233   // <svg> node needing to be recalculated.  (Only applicable in
234   // SVG-as-an-image documents.)
235   virtual void FlushImageTransformInvalidation();
236 
237   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
238 
239   // Returns true IFF our attributes are currently overridden by a <view>
240   // element and that element's ID matches the passed-in string.
IsOverriddenBy(const nsAString & aViewID)241   bool IsOverriddenBy(const nsAString &aViewID) const {
242     return mCurrentViewID && mCurrentViewID->Equals(aViewID);
243   }
244 
GetViewportSize()245   svgFloatSize GetViewportSize() const {
246     return svgFloatSize(mViewportWidth, mViewportHeight);
247   }
248 
SetViewportSize(const svgFloatSize & aSize)249   void SetViewportSize(const svgFloatSize& aSize) {
250     mViewportWidth  = aSize.width;
251     mViewportHeight = aSize.height;
252   }
253 
254   // WebIDL
255   already_AddRefed<SVGAnimatedLength> X();
256   already_AddRefed<SVGAnimatedLength> Y();
257   already_AddRefed<SVGAnimatedLength> Width();
258   already_AddRefed<SVGAnimatedLength> Height();
259   float PixelUnitToMillimeterX();
260   float PixelUnitToMillimeterY();
261   float ScreenPixelToMillimeterX();
262   float ScreenPixelToMillimeterY();
263   bool UseCurrentView();
264   float CurrentScale();
265   void SetCurrentScale(float aCurrentScale);
266   already_AddRefed<nsISVGPoint> CurrentTranslate();
267   void SetCurrentTranslate(float x, float y);
268   uint32_t SuspendRedraw(uint32_t max_wait_milliseconds);
269   void UnsuspendRedraw(uint32_t suspend_handle_id);
270   void UnsuspendRedrawAll();
271   void ForceRedraw();
272   void PauseAnimations();
273   void UnpauseAnimations();
274   bool AnimationsPaused();
275   float GetCurrentTime();
276   void SetCurrentTime(float seconds);
277   void DeselectAll();
278   already_AddRefed<DOMSVGNumber> CreateSVGNumber();
279   already_AddRefed<DOMSVGLength> CreateSVGLength();
280   already_AddRefed<SVGAngle> CreateSVGAngle();
281   already_AddRefed<nsISVGPoint> CreateSVGPoint();
282   already_AddRefed<SVGMatrix> CreateSVGMatrix();
283   already_AddRefed<SVGIRect> CreateSVGRect();
284   already_AddRefed<SVGTransform> CreateSVGTransform();
285   already_AddRefed<SVGTransform> CreateSVGTransformFromMatrix(SVGMatrix& matrix);
286   using nsINode::GetElementById; // This does what we want
287   already_AddRefed<SVGAnimatedRect> ViewBox();
288   already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio();
289   uint16_t ZoomAndPan();
290   void SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv);
291   virtual nsSVGViewBox* GetViewBox() override;
292 
293 private:
294   // nsSVGElement overrides
295 
296   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
297                               nsIContent* aBindingParent,
298                               bool aCompileEventHandlers) override;
299   virtual void UnbindFromTree(bool aDeep, bool aNullParent) override;
300 
301   // implementation helpers:
302 
303   SVGViewElement* GetCurrentViewElement() const;
304 
305   // Methods for <image> elements to override my "PreserveAspectRatio" value.
306   // These are private so that only our friends (AutoSVGRenderingState in
307   // particular) have access.
308   void SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR);
309   void ClearImageOverridePreserveAspectRatio();
310 
311   // Set/Clear properties to hold old version of preserveAspectRatio
312   // when it's being overridden by an <image> element that we are inside of.
313   bool SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR);
314   const SVGPreserveAspectRatio* GetPreserveAspectRatioProperty() const;
315   bool ClearPreserveAspectRatioProperty();
316 
317   void SetIsPaintingForSVGImageElement(bool aIsPaintingSVGImageElement);
318 
IsRoot()319   bool IsRoot() const {
320     NS_ASSERTION((IsInUncomposedDoc() && !GetParent()) ==
321                  (OwnerDoc() && (OwnerDoc()->GetRootElement() == this)),
322                  "Can't determine if we're root");
323     return IsInUncomposedDoc() && !GetParent();
324   }
325 
326   /**
327    * Returns true if this is an SVG <svg> element that is the child of
328    * another non-foreignObject SVG element.
329    */
IsInner()330   bool IsInner() const {
331     const nsIContent *parent = GetFlattenedTreeParent();
332     return parent && parent->IsSVGElement() &&
333            !parent->IsSVGElement(nsGkAtoms::foreignObject);
334   }
335 
336   /*
337    * While binding to the tree we need to determine if we will be the outermost
338    * <svg> element _before_ the children are bound (as they want to know what
339    * timed document root to register with) and therefore _before_ our parent is
340    * set (both actions are performed by Element::BindToTree) so we
341    * can't use GetOwnerSVGElement() as it relies on GetParent(). This code is
342    * basically a simplified version of GetOwnerSVGElement that uses the parent
343    * parameters passed in instead.
344    */
345   bool WillBeOutermostSVG(nsIContent* aParent,
346                           nsIContent* aBindingParent) const;
347 
348   // invalidate viewbox -> viewport xform & inform frames
349   void InvalidateTransformNotifyFrame();
350 
351   // Returns true if we have at least one of the following:
352   // - a (valid or invalid) value for the preserveAspectRatio attribute
353   // - a SMIL-animated value for the preserveAspectRatio attribute
354   bool HasPreserveAspectRatio();
355 
356  /**
357   * Returns the explicit viewBox rect, if specified, or else a synthesized
358   * viewBox, if appropriate, or else a viewBox matching the dimensions of the
359   * SVG viewport.
360   */
361   nsSVGViewBoxRect GetViewBoxWithSynthesis(
362       float aViewportWidth, float aViewportHeight) const;
363   /**
364    * Returns the explicit or default preserveAspectRatio, unless we're
365    * synthesizing a viewBox, in which case it returns the "none" value.
366    */
367   SVGPreserveAspectRatio GetPreserveAspectRatioWithOverride() const;
368 
369   virtual LengthAttributesInfo GetLengthInfo() override;
370 
371   enum { ATTR_X, ATTR_Y, ATTR_WIDTH, ATTR_HEIGHT };
372   nsSVGLength2 mLengthAttributes[4];
373   static LengthInfo sLengthInfo[4];
374 
375   virtual EnumAttributesInfo GetEnumInfo() override;
376 
377   enum { ZOOMANDPAN };
378   nsSVGEnum mEnumAttributes[1];
379   static nsSVGEnumMapping sZoomAndPanMap[];
380   static EnumInfo sEnumInfo[1];
381 
382   virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override;
383 
384   nsSVGViewBox                   mViewBox;
385   SVGAnimatedPreserveAspectRatio mPreserveAspectRatio;
386 
387   // mCurrentViewID and mSVGView are mutually exclusive; we can have
388   // at most one non-null.
389   nsAutoPtr<nsString>            mCurrentViewID;
390   nsAutoPtr<SVGView>             mSVGView;
391 
392   // The size of the rectangular SVG viewport into which we render. This is
393   // not (necessarily) the same as the content area. See:
394   //
395   //   http://www.w3.org/TR/SVG11/coords.html#ViewportSpace
396   //
397   // XXXjwatt Currently only used for outer <svg>, but maybe we could use -1 to
398   // flag this as an inner <svg> to save the overhead of GetCtx calls?
399   // XXXjwatt our frame should probably reset these when it's destroyed.
400   float mViewportWidth, mViewportHeight;
401 
402   // The time container for animations within this SVG document fragment. Set
403   // for all outermost <svg> elements (not nested <svg> elements).
404   nsAutoPtr<nsSMILTimeContainer> mTimedDocumentRoot;
405 
406   // zoom and pan
407   // IMPORTANT: see the comment in RecordCurrentScaleTranslate before writing
408   // code to change any of these!
409   SVGPoint mCurrentTranslate;
410   float    mCurrentScale;
411   SVGPoint mPreviousTranslate;
412   float    mPreviousScale;
413 
414   // For outermost <svg> elements created from parsing, animation is started by
415   // the onload event in accordance with the SVG spec, but for <svg> elements
416   // created by script or promoted from inner <svg> to outermost <svg> we need
417   // to manually kick off animation when they are bound to the tree.
418   bool     mStartAnimationOnBindToTree;
419   bool     mImageNeedsTransformInvalidation;
420   bool     mIsPaintingSVGImageElement;
421   bool     mHasChildrenOnlyTransform;
422 };
423 
424 } // namespace dom
425 
426 // Helper class to automatically manage temporary changes to an SVG document's
427 // state for rendering purposes.
428 class MOZ_RAII AutoSVGRenderingState
429 {
430 public:
AutoSVGRenderingState(const Maybe<SVGImageContext> & aSVGContext,float aFrameTime,dom::SVGSVGElement * aRootElem MOZ_GUARD_OBJECT_NOTIFIER_PARAM)431   AutoSVGRenderingState(const Maybe<SVGImageContext>& aSVGContext,
432                         float aFrameTime,
433                         dom::SVGSVGElement* aRootElem
434                         MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
435     : mHaveOverrides(aSVGContext.isSome() &&
436                      aSVGContext->GetPreserveAspectRatio().isSome())
437     , mRootElem(aRootElem)
438   {
439     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
440     MOZ_ASSERT(mRootElem, "No SVG node to manage?");
441     if (mHaveOverrides) {
442       // Override preserveAspectRatio in our helper document.
443       // XXXdholbert We should technically be overriding the helper doc's clip
444       // and overflow properties here, too. See bug 272288 comment 36.
445       mRootElem->SetImageOverridePreserveAspectRatio(
446           *aSVGContext->GetPreserveAspectRatio());
447       mRootElem->SetIsPaintingForSVGImageElement(
448           aSVGContext->IsPaintingForSVGImageElement());
449     }
450 
451     mOriginalTime = mRootElem->GetCurrentTime();
452     mRootElem->SetCurrentTime(aFrameTime); // Does nothing if there's no change.
453   }
454 
~AutoSVGRenderingState()455   ~AutoSVGRenderingState()
456   {
457     mRootElem->SetCurrentTime(mOriginalTime);
458     if (mHaveOverrides) {
459       mRootElem->ClearImageOverridePreserveAspectRatio();
460       mRootElem->SetIsPaintingForSVGImageElement(false);
461     }
462   }
463 
464 private:
465   const bool mHaveOverrides;
466   float mOriginalTime;
467   const RefPtr<dom::SVGSVGElement> mRootElem;
468   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
469 };
470 
471 } // namespace mozilla
472 
473 #endif // SVGSVGElement_h
474